25 Jun 2023

Building Restful apis with Python and Flask Fastapi

Building RESTful APIs has become an important part of web development, as it allows developers to create web services that can be easily consumed by other applications. In this blog, we will explore how to build RESTful APIs using Python and two popular frameworks - Flask and FastAPI.

What is RESTful API?

REST stands for Representational State Transfer, which is a software architectural style for building scalable and loosely coupled web services. RESTful APIs are designed to be lightweight, scalable, and easily consumable by other applications. RESTful APIs use HTTP requests to perform CRUD (Create, Read, Update, Delete) operations on data resources.

Why use Python for RESTful API development?

Python is a popular programming language with a large and active community. It offers a wide range of libraries and frameworks that make it easy to develop RESTful APIs. Python is also known for its simplicity, readability, and ease of use, which makes it a great choice for building web services.

Flask vs FastAPI: Which one to choose?

Flask and FastAPI are two popular Python frameworks for building RESTful APIs. Flask is a lightweight and flexible framework that is easy to use and can be used to build small to medium-sized APIs. On the other hand, FastAPI is a modern and fast framework that is built on top of the latest Python features and can be used to build large and complex APIs.

Both Flask and FastAPI have their own advantages and disadvantages. Flask is a mature and stable framework that has been around for a long time and has a large community. FastAPI, on the other hand, is a relatively new framework that is gaining popularity due to its speed and modern features.

In this blog, we will explore both Flask and FastAPI and learn how to build RESTful APIs using both frameworks.

Building a RESTful API with Flask

To get started with Flask, we need to install it using pip:

pip install Flask

After installing Flask, we can create a new Flask application:

from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello():
    return 'Hello World!'

if __name__ == '__main__':
    app.run()

In this example, we created a new Flask application and defined a simple route that returns 'Hello World!' when the root URL is accessed. We then started the application using the run() method.

To create a RESTful API with Flask, we need to define routes that map to different HTTP methods. For example, to create a route for creating a new resource, we can use the POST method:

@app.route('/api/users', methods=['POST'])
def create_user():
    # create a new user
    return 'User created!'

In this example, we defined a new route /api/users that maps to the create_user() function. We also specified that this route should only be accessible using the POST method.

To read data from a resource, we can use the GET method:

@app.route('/api/users/<int:user_id>', methods=['GET'])
def get_user(user_id):
    # get the user with the specified ID
    return f'User {user_id} fetched!'

In this example, we defined a new route /api/users/<int:user_id> that maps to the get_user() function. We also specified that this route should only be accessible using the GET method. The <int:user_id> part of the route specifies that the user_id parameter should be an integer.

To update a resource, we can use the PUT method:

@app.route('/api/users/int:user_id', methods=['PUT'])
def update_user(user_id):
  # update the user with the specified ID
  return f'User {user_id} updated!'

In this example, we defined a new route /api/users/<int:user_id> that maps to the update_user() function. We also specified that this route should only be accessible using the PUT method. The <int:user_id> part of the route specifies that the user_id parameter should be an integer.

To delete a resource, we can use the DELETE method:

@app.route('/api/users/<int:user_id>', methods=['DELETE'])
def delete_user(user_id):
    # delete the user with the specified ID
    return f'User {user_id} deleted!'

In this example, we defined a new route /api/users/<int:user_id> that maps to the delete_user() function. We also specified that this route should only be accessible using the DELETE method. The <int:user_id> part of the route specifies that the user_id parameter should be an integer.

We can also return JSON data from our API using the jsonify() method:

from flask import jsonify

@app.route('/api/users', methods=['GET'])
def get_users():
    # get a list of users
    users = [{'id': 1, 'name': 'John Doe'}, {'id': 2, 'name': 'Jane Doe'}]
    return jsonify(users)

In this example, we defined a new route /api/users that maps to the get_users() function. We returned a list of users in JSON format using the jsonify() method.

Building a RESTful API with FastAPI

To get started with FastAPI, we need to install it using pip:

pip install fastapi

We also need to install the uvicorn server, which is used to run FastAPI applications:

pip install uvicorn

After installing FastAPI, we can create a new FastAPI application:

from fastapi import FastAPI

app = FastAPI()

@app.get('/')
def hello():
    return {'message': 'Hello World!'}

In this example, we created a new FastAPI application and defined a simple route that returns a JSON object containing the message 'Hello World!' when the root URL is accessed.

To create a RESTful API with FastAPI, we need to define routes that map to different HTTP methods. For example, to create a route for creating a new resource, we can use the POST method:

@app.post('/api/users')
def create_user():
    # create a new user
    return {'message': 'User created!'}

In this example, we defined a new route /api/users that maps to the create_user() function. We also specified that this route should only be accessible using the POST method.

To read data from a resource, we can use the GET method:

@app.get('/api/users/{user_id}')
def get_user(user_id: int):
    # get the user with the specified ID
    return {'message': f'User {user_id} fetched!'}

In this example, we defined a new route /api/users/{user_id} that maps to the get_user() function. We also specified that this route should only be accessible using the GET method. The {user_id} part of the route specifies that the user_id parameter should be a path parameter of type integer.

To update a resource, we can use the PUT method:

@app.put('/api/users/{user_id}')
def update_user(user_id: int):
  # update the user with the specified ID
  return {'message': f'User {user_id} updated!'}

In this example, we defined a new route /api/users/{user_id} that maps to the update_user() function. We also specified that this route should only be accessible using the PUT method. The {user_id} part of the route specifies that the user_id parameter should be a path parameter of type integer.

To delete a resource, we can use the DELETE method:

@app.delete('/api/users/{user_id}')
def delete_user(user_id: int):
    # delete the user with the specified ID
    return {'message': f'User {user_id} deleted!'}

In this example, we defined a new route /api/users/{user_id} that maps to the delete_user() function. We also specified that this route should only be accessible using the DELETE method. The {user_id} part of the route specifies that the user_id parameter should be a path parameter of type integer.

We can also use FastAPI's data models to define the structure of our data. For example, we can define a data model for a user like this:

from pydantic import BaseModel

class User(BaseModel):
    id: int
    name: str
    email: str

In this example, we defined a User data model that has three fields: id, name, and email.

We can use this data model to define the structure of the data that is sent in requests and responses. For example, we can update our create_user() function to accept a User object in the request body:

@app.post('/api/users')
def create_user(user: User):
    # create a new user
    return {'message': 'User created!', 'user': user}

In this example, we updated our create_user() function to accept a User object in the request body. We also updated the response to include the created user in the response body.

We can also use FastAPI's Response class to return custom HTTP responses. For example, we can return a 201 Created response when a new resource is created:

from fastapi import Response

@app.post('/api/users')
def create_user(user: User, response: Response):
    # create a new user
    response.status_code = 201
    response.headers['Location'] = f'/api/users/{user.id}'
    return {'message': 'User created!', 'user': user}

In this example, we updated our create_user() function to return a custom 201 Created response. We set the status code to 201 and added a Location header to indicate the URL of the created resource.

Conclusion

In this blog post, we learned how to build RESTful APIs with Python and two popular web frameworks: Flask and FastAPI. We learned how to define routes that map to different HTTP methods, how to work with JSON data, and how to use data models to define the structure of our data. We also learned how to return custom HTTP responses using Flask and FastAPI.

RESTful APIs are a powerful tool for building web applications and services. They allow us to create resources that can be accessed and manipulated using standard HTTP methods, making it easy to build applications that can be consumed by a wide range of clients. By using Python and Flask/FastAPI, we can build robust and scalable RESTful APIs that can be used in a variety of contexts.