Guide to creating a RESTful API using Python, Flask and MongoDB
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 resources like 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, update and delete the documents (which will contain User information) from a Mongo database using Python and Flask framework.
Getting started
Here are the tools we’ll need to build our APIs:
- Python 3.7
- Postman Download here
- Mongo Database Documentation
- Robo 3T Download here
Creating your local environment
NOTE: While working with Python, we would recommend to use virtual environment to keep all the project’s dependencies isolated from other projects.
conda create -n restfulapi python=3.7 anaconda # Create the environment
source activate restfulapi # Activate the environment
Install dependencies
pip install -r requirements.txt
Start the MongoDB server
If you’re using MacOS, you could use brew
to start the server.
brew services start mongodb
Collections in MongoDB
A collection is similar to a table in a traditional relational Db. We will created a Users collection to store 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 of
the 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
try:
body = ast.literal_eval(json.dumps(request.get_json()))
except:
# Bad request as request body is not available
# Add message for debugging purpose
return "", 400
record_created = collection.insert(body)
# Prepare the response
if isinstance(record_created, list):
# Return list of Id of the newly created item
return jsonify([str(v) for v in record_created]), 201
else:
# Return Id of the newly created item
return jsonify(str(record_created)), 201
except:
# Error while trying to create the resource
# Add message for debugging purpose
return "", 500
A single user returns the entity id so the frontend can reference the newly created item. On the other hand, an array of users returns an array of entity id.
Read Users
To fetch a list users which we just created, we will design an endpoint
GET /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.
"""
try:
# Call the function to get the query params
query_params = helper_module.parse_query_params(request.query_string)
# Check if dictionary is not empty
if query_params:
# Try to convert the value to int
query = {k: int(v) if isinstance(v, str) and v.isdigit() else v for k, v in query_params.items()}
# Fetch all the record(s)
records_fetched = collection.find(query)
# Check if the records are found
if records_fetched.count() > 0:
# Prepare the response
return dumps(records_fetched)
else:
# No records are found
return "", 404
# If dictionary is empty
else:
# Return all the records as query string parameters are not available
if collection.find().count() > 0:
# Prepare response if the users are found
return dumps(collection.find())
else:
# Return empty array if no users are found
return jsonify([])
except:
# Error while trying to fetch the resource
# Add message for debugging purpose
return "", 500
We will return the user details if the search criteria is provided else we would try to return all the documents if exists else we would return an empty array.
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(user_id):
"""
Function to update the user.
"""
try:
# Get the value which needs to be updated
try:
body = ast.literal_eval(json.dumps(request.get_json()))
except:
# Bad request as the request body is not available
# Add message for debugging purpose
return "", 400
# Updating the user
records_updated = collection.update_one({"id": int(user_id)}, body)
# Check if resource is updated
if records_updated.modified_count > 0:
# Prepare the response as resource is updated successfully
return "", 200
else:
# Bad request as the resource is not available to update
# Add message for debugging purpose
return "", 404
except:
# Error while trying to update the resource
# Add message for debugging purpose
return "", 500
For example, we would like to update the document with id
matches 1
.
Remove Users
Finally, we will design an endpoint DELETE /api/v1/users/<user_id>
to delete the user
from the database.
@app.route("/api/v1/users/<user_id>", methods=['DELETE'])
def remove_user(user_id):
"""
Function to remove the user.
"""
try:
# Delete the user
delete_user = collection.delete_one({"id": int(user_id)})
if delete_user.deleted_count > 0 :
# Prepare the response
return "", 204
else:
# Resource Not found
return "", 404
except:
# Error while trying to delete the resource
# Add message for debugging purpose
return "", 500
We would specify the matching condition on which to delete the document.
For example, we would like to remove the document with id
matches 1
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 the user with notFound 404 status."""
# Message to the user
message = {
"err":
{
"msg": "This route is currently not supported. Please refer API documentation."
}
}
# Making the message looks good
resp = jsonify(message)
# Sending OK response
resp.status_code = 404
# Returning the object
return resp
If you would like to play around with the data, you could also use Robo 3T mongo shell.
To see the RESTful API in action, you can git clone and run this example app from 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
Moesif is the most advanced API Analytics platform. Thousands of platform companies leverage Moesif for debugging, monitoring and discovering insights.
Learn More