DEV Community

Xing Wang
Xing Wang

Posted on • Originally published at moesif.com on

Guide to creating a RESTful API using Python, Flask, and MongoDB

Note: this article is written by my colleague Keyur. We blog at the same place

Intro to RESTful APIs

RESTful APIs using JSON on the HTTP protocol is by far the most popular approach to creating web APIs.They enable developers to think in terms of resources with actions on those resourceslike CRUD (Create, Read, Update, Delete). However, there are upcoming APIs like GraphQL which is increasingly becoming popular with many applications.

In this tutorial, we are going to build a RESTful API to create, read, updateand delete the documents (which will contain User information) from a Mongo databaseusing Python and Flask framework.

Getting started

Here are the tools we’ll need to build our APIs:

Creating your local environment

NOTE:While working with Python, we would recommend to use virtual environmentto keep all the project’s dependencies isolated from other projects.

conda create -n restfulapi python=2.7 anaconda # Create the environment
source activate restfulapi # Activate the environment

Enter fullscreen mode Exit fullscreen mode

Install dependencies

pip install -r requirements.txt

Enter fullscreen mode Exit fullscreen mode

Start the MongoDB server

If you’re using MacOS, you could use brew to start the server.

brew services start mongodb

Enter fullscreen mode Exit fullscreen mode

Collections in MongoDB

A collection is similar to a table in a traditional relational Db. We will createda Users collection tostore user details similar to a Users table in SQL.

Create Users

We will create an endpoint POST /api/v1/users which takes in a JSON object consisting ofthe user details like name, email, phone as JSON in the request body.We could also design the endpoint to take an array of users.

@app.route("/api/v1/users", methods=['POST'])
def create_user():
    """
       Function to create new users.
    """
    try:
      # Create new users
      body = ast.literal_eval(json.dumps(request.get_json()))
      record_created = collection.insert(body)
      body._id = record_created.inserted_id
      # Prepare the response
      return jsonify(body)
    except:
      # Something went wrong server side, so return Internal Server Error.
      resp.status_code = 500
      return resp

Enter fullscreen mode Exit fullscreen mode

create_user.png

If this is a batch endpoint taking in an array of users, we could return the number of users created.On the other hand, a single user may return the entity id so the frontend can reference the newlycreated item.

Read Users

To fetch a list users which we just created, we will design an endpointGET /api/v1/users and pass the search criteria as the query string parameters.

@app.route("/api/v1/users", methods=['GET'])
def fetch_users():
    """
       Function to fetch the users.
    """
    # Prepare the response
    try:
      # Call the function to get the query params
      query_params = helper_module.parse_query_params(request.query_string)
      key = query_params.keys()[0]
      value = query_params.values()[0]

      # Encode params
      key, value = helper_module.encode_params(key, value)

      # Fetch all the record(s)
      records_fetched = collection.find({key: value})

      if len(records_fetched) > 0:
        # Prepare the response
        resp = jsonify(records_fetched)
        return resp
      else:
        # No records found, so we can return an empty list (Added for example)
        return jsonify([])
    except:
      # Something went wrong server side, so return Internal Server Error.
      resp.status_code = 500
      return resp

Enter fullscreen mode Exit fullscreen mode

We will return the user details if the matching document is found else wewould return an empty array.

fetch_user.png

Update Users

Now, we will design an endpoint POST /api/v1/users/<user_id> to update the user details.

@app.route("/api/v1/users/<user_id>", methods=['POST'])
def update_user():
    """
       Function to update the user.
    """
     try:
          # Update the user
          body = ast.literal_eval(json.dumps(request.get_json()))
          record_updated = collection.update_one({"_id": user_id}, body)

          # Prepare the response
          resp = jsonify(body)
      except:
        resp.status_code = 500
        # Return the object
        return resp

Enter fullscreen mode Exit fullscreen mode

UPDATE Request:

update_user.png

Remove Users

Finally, we will design an endpoint DELETE /api/v1/users/<user_id> to delete the userfrom the database.

@app.route("/api/v1/users/<user_id>", methods=['DELETE'])
def remove_user():
    """
       Function to remove the user.
    """
    try:
        # Delete all the users matched
        body = ast.literal_eval(json.dumps(request.get_json()))
        record_deleted = collection.delete_one({ "_id" : user_id })

        # Prepare the response
        if result.deleted_count > 0
          # We return 204 No Content to imply resource updated successfully without returning
          # the deleted entity.
          resp.status_code = 204
          return resp
        else:
          # Entity not found, perhaps already deleted, return 404
          resp.status_code = 404
          return resp
    except:
      # Something went wrong server side, so return Internal Server Error.
      resp.status_code = 500
      return resp

Enter fullscreen mode Exit fullscreen mode

We would specify the matching condition on which to delete the document in therequest body. For example, we would like to remove the document with id is 3

remove_user.png

Once the users are deleted, we would return the number of deleted users messageto the client.

Handling page not found request

We recommend to handle page not found request with informative message to the user.

@app.errorhandler(404)
def page_not_found(e):
    """Send message to user with notFound 404 status."""
    # Message to the user
    message = {
        "message": "Message from the API",
        "errors": [
            {
                "message": "This route is not currently supported. Please refer API documentation."
            }
        ]
    }
    # Make the message looks good
    resp = jsonify(message)
    # Send OK response
    resp.status_code = 404
    # Return the object
    return resp

Enter fullscreen mode Exit fullscreen mode

If you would like to play around the data, you could also use Robo 3T mongo shell.

mongo_shell.png

To see the RESTful API in action, you can git clone and run this example appfrom GitHub

In the next blog, we would learn about how to authorize and authenticate our APIs.If you’re curious to get started with GraphQL using Python - please refer to this blog. Meanwhile, if you have any questions, reach out to the Moesif Team

Top comments (0)