Using Flask on a Raspberry Pi with MongoDB (Part 2)

Introduction

This is part two of an article series on how to make a Flask and MongoDB web application, for development purposes, on a Raspberry Pi single board computer as a local web server for your backend data. In the last part we covered how to SSH into the Pi device, as well as install and setup MongoDB on a Raspbian distro of Linux. This article will show how to setup a Flask web server, using about a hundred lines of Python, so that any device on your local network can access the MongoDB data.

SSH into the Raspberry Pi

If you haven’t already, you should connect to the Raspberry Pi, using a secure shell (SSH) command line, so that you can insert documents into a MongoDB collection that the Flask app can serve up.

If you’re unsure of your Pi’s local gateway address you can use the arp -a command on another network computer and look for a MAC address that starts with b8 or dc.

Once you’ve pinpointed the Pi’s IP address use the ssh command to access it using the default pi username:

1
ssh pi@192.168.XXX.xx

Make sure to change the Xs so that they match your Pi’s assigned IP and gateway address.

Screenshot of getting IP addresses for Flask Raspberry Pi MongoDB web app

NOTE: If you get some kind of of connection refused error, then it probably means you haven’t enabled SSH yet on your Raspbian installation of Linux.

Connect to MongoDB

At this point you should be able to access the mongo Shell CLI without any problems simply by inputting the mongo command into the Raspberry Pi terminal. If you need to connect to a custom host or port you can do so by passing values to the --host and --port options:

1
mongo --host 192.168.100.40 --port 12345

NOTE: The default port for MongoDB is 27017.

Insert many MongoDB documents

You’ll need to insert some MongoDB documents into the server on the Raspberry Pi before you can use them in a web app.

Once MongoDB is running you can access mongo Shell just by typing mongo into the Raspbian terminal prompt. Once you’re inside of mongo Shell execute the use command to initialize the database that the documents will be inserted into:

1
use flaskDb

The use command should return a response of switched to db flaskDb. Now you can just use db as an alias for the database name and insert some documents into a new collection.

Newer versions of MongoDB allow you to do this with the insertMany() method, but the method call wasn’t introduced until v3.2 of MongoDB, so, if your Pi is running an older, ARM-compatible installation of MongoDB, you’ll have to insert documents, one at a time, using the deprecated insert() method instead.

The following mongo Shell example inserts two new documents into a collection called sample:

1
2
db.sample.insert( { str: "hello", int: 1 } );
db.sample.insert( { str: "world", int: 2 } );

Version 2.2 of the mongo Shell shouldn’t return a response, but you can use the collection’s find() method to verify that the data was inserted:

1
db.sample.find();

The above command should output something like the following:

1
2
{ "_id" : ObjectId("5e27f624cb515b2bd00f439d"), "str" : "hello", "int" : 1 }
{ "_id" : ObjectId("5e27f62acb515b2bd00f439e"), "str" : "world", "int" : 2 }

Insert many documents in MongoDB v4.2

If you’re using a version of MongoDB that’s v3.2 (or newer) you can just use the insertMany() method to accomplish the same task:

1
2
3
4
5
6
db.sample.insertMany(
    [
        { str: "hello", int: 1 },
        { str: "world", int: 2 }
    ]
);

The insertMany() command should return something like the following:

1
2
3
4
5
6
7
{
    "acknowledged" : true,
    "insertedIds" : [
        ObjectId("5e27f65ef6adcd893cbad53e"),
        ObjectId("5e27f65ef6adcd893cbad53f")
    ]
}

NOTE: Just use the db.sample.findOne() method call to have mongo Shell v4.2 return a single document.

You can also use the show command to verify that the database and collection were created at the time of the document insertion:

1
show databases

Use the quit() command to exit out of the mongo Shell CLI.

Flask project example

Now it’s time to create the Flask project for the MongoDB web application. Navigate to the directory for your Flask-MongoDB project, or create one, using the mkdir command, like in the following example:

1
mkdir flask-mongodb-raspberry && cd flask-mongodb-raspberry

Once you’re inside the project directory you’ll need to create a new Python script for the Flask application.

Create a Python script

Use the touch command to create a new Python script for the Flask app like so:

1
touch app.py

The above command should have created the app.py file in your current directory.

SCP command to push files to the Raspberry Pi

You can also create the script locally and use the scp secure copy command to push the script to your Raspberry Pi after you’ve developed it in an IDE on your dev machine:

1
scp /local/path/app.py pi@{PI_IP_ADDRESS}:/final/pi/path/app.py

An example of how you can push the Python script to the Raspberry Pi server will look something like this:

1
scp $PWD/app.py pi@192.168.100.40:/var/www/flask-mongo-app/app.py

You can also just open a text-based editor like nano, vi, or gedit on your Raspberry Pi and paste the contents of the Python script into it.

Import Flask in the Python script

At the top of your Python script you’ll need to import the necessary libraries for the web app:

1
2
3
4
5
# import the Flask library
from flask import Flask

# import the MongoClient class
from pymongo import MongoClient

Declare global variables for the Flask app

The next step should be to declare some globals for your Flask application, and for your MongoDB server:

1
2
3
DOMAIN = "192.168.100.40"
MONGO_PORT = 27017
FLASK_PORT = 5000 # default port is 5000

NOTE: If you want to use a custom Flask port, rather than the default port of 5000, you’ll have to edit the /etc/mongodb.conf file and hardcode your Pi’s IP address and gateway into the bind_ip value and restart the MongoDB service with sudo service mongodb restart. Be aware that anytime the Pi’s IP address changes you’ll have to re-modify that configuration file, and restart the MongoDB server.

Create an app instance of the Flask class for the web application:

1
2
# create new app instance using Flask class constructor
mongo_app = Flask(__name__)

PyMongo connection example

Now let’s declare a function that will attempt to connect to the Pi’s MongoDB server and, if successful, it will dump all of documents into the collection object:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def get_mongo_docs(arg=''):

    # declare an empty dict for the MongoDB documents
    documents = {}

    try:
        # create a new client instance of MongoClient
        client = MongoClient(
            host = [str(DOMAIN) + ":" + str(MONGO_PORT)]
        )

        # calling server_info() will raise an exception if client timeout
        print ("\nserver_info():", client.server_info()["versionArray"])

    except Exception as err:

        # reset the client instance to 'None' in case of timeout exception
        client = None

        # print pymongo.errors.ServerSelectionTimeoutError
        print ("MongoClient error:", err)

PyMongo cursor for the Flask app data

If the client connect was successful the script will iterate over the documents in a for iteration loop and nest them inside of an outer dictionary:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    if client != None:

        # declare database and collection instances
        db = client.flaskDb
        col = db.sample

        for doc in col.find():
            print ("\n", doc)

            # should return a dict object
            print (type(doc))

            # nest the document inside of the original dictionary
            documents["id"] = doc

    # return the nested documents
    return documents

Python function for the Flask HTML

The following is a simple example of how you can declare a function that will concatenate an HTML string for the Flask app that will ultimately be returned to the frontend user:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def get_app_html(new_html=''):

    # define a string for the HTML
    html = """
    <!DOCTYPE html>
    <html>
    <head>
    <title>My first MongoDB Flask web app</title>
    <h1>ObjectRocket MongoDB App</h1>
    </head>
    <br>
    """


    html += new_html

    html += "<br></body>\n</html>"
    return html

The above function allows for further HTML to be appended to the middle of the document before closing the HTML tags and returning the string.

Flask app with routes to multiple URLs

This simple app example will use two routes, and both will perform only the GET HTTP method. The first is the basic route for the app’s “homepage” that returns only the basic webpage’s HTML:

1
2
3
4
5
6
@mongo_app.route('/')
def home_page():

    html = get_app_html()

    return html

The second route is designed to return MongoDB documents as HTML by calling the get_mongo_docs(), that we declared earlier, and then iterating over the nested dictionary results in order to concatenate a new string that formats the document data by enclosing them in <h3> and <h4> HTML tags:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@mongo_app.route("/api/v1/docs/", methods=['GET'])
def get_docs():

    # declare string for new HTML
    new_html = "<h2>MongoDB documents returned:</h2><br>"

    # call the function to get MongoDB documents
    docs = get_mongo_docs()

    # iterate over the nested dict of docs
    for k, doc in docs.items():

        # iterate over each document's values
        for key, values in doc.items():

            # append the doc data to the new HTML
            new_html += "<h3>Key: " + str(key) + "</h3>"
            new_html += "<h4>Values: " + str(values) + "</h4><br>"

    # pass the new HTML string to the function call
    html = get_app_html( new_html )

    return html

Run the Flask Raspberry Pi app

The last part of the code is to instruct Python to run the Flask application module as the main program when you execute the script:

1
2
3
4
5
6
7
if __name__ == '__main__':

    # run the application
    mongo_app.run(
        host = DOMAIN,
        port = FLASK_PORT
    )

NOTE: The host and port parameters are optional, and if you don’t pass any values to them the Flask app will run on http://127.0.0.1:5000 by default.

Run the Python script from command line

You can just run the Flask application using the flask run command in your terminal which allows you to pass a custom host domain to it using the --host option:

1
flask run --host=192.168.100.40

NOTE: You may get an error saying: ..this version of PyMongo requires at least 2.., which probably means you’ll have to install an older version of the PyMongo driver with PIP. If you’re running MongoDB v2.4, then use the 2.9.5 version of PyMongo, and install it with pip3 install pymongo==2.9.5. Use the mongod --version command in your Pi terminal to check the version of your server.

You can also just run the Python script for the Flask app in development mode by executing: python3 app.py.

It should return a response that looks like the following:

1
2
3
4
5
6
 * Serving Flask app "app" (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://192.168.100.40:5000/ (Press CTRL+C to quit)

Just navigate to http://192.168.100.40:5000/api/v1/docs/ in any browser on any devices connected to your local network, and you should see the MongoDB documents you inserted earlier displayed in the webpage.

Screenshot of the Flask Raspberry Pi MongoDB web app returning documents in a browser

When you’re finished testing the app just press CTRL+C in your terminal window to shutdown the Flask server.

Conclusion

The cool thing about using a Raspberry Pi for a web server is that any device on your local network will now be able to access the web app by navigating to the single board computer’s IP address in a browser tab, and you don’t even have to pay for hosting fees, or mess with your local dev environment.

Check out the complete Python script for the Flask web application below.

Just the Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#!/usr/bin/env python3
#-*- coding: utf-8 -*-

# import the Flask library
from flask import Flask

# import the MongoClient class
from pymongo import MongoClient

# declare globals for PyMongo's client host
DOMAIN = "192.168.100.40"
MONGO_PORT = 27017
FLASK_PORT = 5000

# create new app instance using Flask class constructor
mongo_app = Flask(__name__)

def get_mongo_docs(arg=''):

    # declare an empty dict for the MongoDB documents
    documents = {}

    try:
        # create a new client instance of MongoClient
        client = MongoClient(
            host = [str(DOMAIN) + ":" + str(MONGO_PORT)]
        )

        # calling server_info() will raise an exception if client timeout
        print ("\nserver_info():", client.server_info()["versionArray"])

    except Exception as err:

        # reset the client instance to 'None' in case of timeout exception
        client = None

        # print pymongo.errors.ServerSelectionTimeoutError
        print ("MongoClient error:", err)

    if client != None:

        # declare database and collection instances
        db = client.flaskDb
        col = db.sample

        for doc in col.find():
            print ("\n", doc)

            # should return a dict object
            print (type(doc))

            # nest the document inside of the original dictionary
            documents["id"] = doc

    # return the nested documents
    return documents

def get_app_html(new_html=''):

    # define a string for the HTML
    html = """
    <!DOCTYPE html>
    <html>
    <head>
    <title>My first MongoDB Flask web app</title>
    <h1>ObjectRocket MongoDB App</h1>
    </head>
    <br>
    """


    html += new_html

    html += "<br></body>\n</html>"
    return html

# set the routes for the application
@mongo_app.route('/')
def home_page():

    html = get_app_html()

    return html

@mongo_app.route("/api/v1/docs/", methods=['GET'])
def get_docs():

    # declare string for new HTML
    new_html = "<h2>MongoDB documents returned:</h2><br>"

    # call the function to get MongoDB documents
    docs = get_mongo_docs()

    # iterate over the nested dict of docs
    for k, doc in docs.items():

        # iterate over each document's values
        for key, values in doc.items():

            # append the doc data to the new HTML
            new_html += "<h3>Key: " + str(key) + "</h3>"
            new_html += "<h4>Values: " + str(values) + "</h4><br>"

    # pass the new HTML string to the function call
    html = get_app_html( new_html )

    return html

if __name__ == '__main__':

    # run the application
    mongo_app.run(
        host = DOMAIN,
        port = FLASK_PORT
    )

Pilot the ObjectRocket Platform Free!

Try Fully-Managed CockroachDB, Elasticsearch, MongoDB, PostgreSQL (Beta) or Redis.

Get Started

Keep in the know!

Subscribe to our emails and we’ll let you know what’s going on at ObjectRocket. We hate spam and make it easy to unsubscribe.