Open In App

FastAPI Architecture

Last Updated : 04 Nov, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

FastAPI is a cutting-edge Python web framework that simplifies the process of building robust REST APIs. In this article, we will explore the fundamental aspects of architecture by delving into its core components.

What is FastAPI?

FastAPI is a modern, fast (high-performance), web framework for building APIs with Python 3.6+ based on standard Python type hints. It is designed to be easy to use, efficient, and reliable, making it a popular choice for developing RESTful APIs and web applications. FastAPI has gained popularity due to its simplicity, automatic documentation generation, and excellent performance.

Features of FastAPI

  • Type Safety and Validation: FastAPI uses Python type hints and Pydantic models for automatic data validation and serialization. This results in type-safe APIs where you catch errors at compile time rather than runtime.
  • Fast Performance: FastAPI is one of the fastest web frameworks available. It performs at speeds comparable to Node.js and Go frameworks due to its efficient use of modern Python features.
  • Automatic Interactive API Documentation: FastAPI automatically generates interactive API documentation (Swagger UI and ReDoc) based on your code’s type hints and Pydantic models. This means you get documentation that is always in sync with your code.
  • Dependency Injection System: FastAPI has a robust dependency injection system that simplifies managing complex dependencies like database connections, authentication, and external services. Dependencies are automatically injected into route functions.
  • Asynchronous Support: FastAPI natively supports asynchronous programming, allowing you to write asynchronous route handlers and take full advantage of Python’s async/await syntax for I/O-bound operations.
  • Authentication and Authorization: FastAPI provides built-in support for various authentication methods, including JWT tokens, OAuth2, and API key validation. It also offers authorization decorators to control access to different parts of your API.
  • Data Serialization and Parsing: FastAPI handles automatic serialization of Python objects into JSON responses. It can also parse incoming request data, including query parameters, request bodies (JSON, form data), and headers, based on type hints.
  • Validation and Query Parameter Handling: FastAPI performs automatic validation of query parameters, path parameters, request bodies, and headers based on type hints. It also handles query parameters, allowing you to define optional and required parameters effortlessly.
  • Custom Responses and Status Codes:FastAPI allows you to return custom responses, set specific HTTP status codes, headers, and cookies, giving you fine-grained control over the API responses.
  • WebSockets and Background Tasks: FastAPI supports WebSocket communication and background tasks, allowing you to handle real-time interactions and perform asynchronous tasks in the background.
  • Automatic Dependency Resolution: FastAPI automatically resolves dependencies based on their type hints, ensuring that the required components are instantiated and passed to the route handlers as needed.

Core Components of FastAPI

First, you’ll need to install FastAPI and an ASGI server, such as uvicorn, if you haven’t already. You can install them using pip:

pip install fastapi uvicorn

Endpoints

Endpoints in FastAPI are Python functions that handle incoming HTTP requests. They are defined using the @app.route decorator. Endpoints can have path parameters, query parameters, request bodies, and more. FastAPI automatically handles data validation, serialization, and deserialization based on Python type hints.
The code for endpoints can be found in various places, reflecting the organization of the project. Specifically, endpoints are defined within the “fastapi/routing/ directory”. However, the actual endpoint definitions and route handling are distributed across multiple files due to the modularity of the library.

Overview of where we can find endpoints in the FastAPI repository.

  • Routing Module:The core endpoint logic can be found in the fastapi/routing.py file. This file contains classes and functions responsible for handling routes, request handling, and response generation.
  • Routers: Endpoints are defined in routers. Routers are organized in the fastapi/routers/ directory. Each router file defines endpoints related to specific resources or functionalities.
  • Dependencies:Endpoint dependencies (middleware, authentication, etc.) are defined in the fastapi/dependencies/ directory.
  • Tests: Tests for endpoints and routing logic are located in the tests/test_routers/ directory. These tests provide examples of how endpoints are tested in the FastAPI library.
    Here @app.get(“/”) defines an endpoint for the root URL (/) using the GET method. When you make a GET request to the root URL, the read_root() function will be executed. @app.get(“/items/{item_id}”) defines an endpoint for the /items/{item_id} URL using the GET method. {item_id} is a path parameter, and q is a query parameter. The item_id path parameter is automatically parsed from the URL, and the q query parameter has a default value of None. When a request is made to the /items/{item_id} URL (e.g., /items/42?q=test), the read_item() function will be executed, and it will receive the item_id from the URL path and the optional q query parameter.

Python3




from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
    return {"message": "Hello, World!"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: str = None):
    return {"item_id": item_id, "q": q}


Request Handling

When a request is received, FastAPI automatically parses and validates query parameters, request bodies (JSON, form data), path parameters, and headers using Python type hints. Data validation and serialization are automatically handled, reducing the boilerplate code required for input validation.
In FastAPI, request handling logic is primarily defined in router modules. Routers handle incoming HTTP requests, and their logic is defined in separate Python files within the routers directory of the FastAPI repository. Each router file contains endpoint definitions, request handling functions, and route configurations. You can find request handling logic in various router files within the FastAPI repository’s routers directory.

Example: Create a file (for example, main.py) with the following code:

We import the FastAPI class and create an instance of it.We define a Pydantic model Item to represent the expected request data. Pydantic models are used for data validation and serialization.We create a route using the @app.post(“/items/”) decorator. The create_item function is the request handler for this route. It takes an Item object as input.Inside the create_item function, we create a dictionary containing the received item data.The function returns this dictionary, which FastAPI automatically serializes into JSON before sending it as the response.

Python3




from fastapi import FastAPI
app = FastAPI()
class Item:
    def __init__(self, name, description, price):
        self.name = name
        self.description = description
        self.price = price
@app.post("/items/")
async def create_item(item: Item):
    """
    Create an item with a name, description, and price.
    """
    item_dict = {
        "name": item.name,
        "description": item.description,
        "price": item.price,
    }
    return item_dict


To run the FastAPI application, use the following command:

uvicorn main:app --reload

This command starts the ASGI server (uvicorn) and tells it to run the app instance from the main.py file. The –reload flag enables automatic code reloading during development.

You can test the API by sending a POST request to http://localhost:8000/items/ with JSON data containing “name”, “description”, and “price”. FastAPI will handle the request, validate the input data, and serialize the output data back to JSON before sending the response.
If you send this post request
curl -X POST -H “Content-Type: application/json” -d ‘{“name”: “Example Item”, “description”: “Test Description”, “price”: 19.99}’ http://localhost:8000/items/

Screenshot-2023-10-09-132300

Ouput for the above code

We can see that post request is handled successfully and get request is not mentioned it is failed without giving error on the user side.

Data Models (Pydantic Models)

In FastAPI, Pydantic models are used for defining the data structures of your application. These models not only serve as a powerful tool for data validation but also play a central role in automatic documentation generation and serialization/deserialization of data. Pydantic models are typically defined in the schemas directory within the FastAPI application, and they help ensure that the data sent to and received from the API endpoints adhere to the specified structure and constraints.
In the FastAPI repository, you can find Pydantic models defined in the schemas directory within the respective router modules. For example, if you have an items router handling item-related endpoints, you might define a Pydantic model for items in a file like schemas/item.py

Dependency Injection System

FastAPI provides a powerful dependency injection system, allowing you to declare dependencies for your endpoints. Dependencies can be used for authentication, database connections, external services, etc. FastAPI manages the instantiation and lifecycle of these dependencies.
The internal implementation of FastAPI’s dependency injection system involves a combination of Python’s type hinting, function annotations, and a mechanism to inspect these annotations during the request handling process. Overview of how FastAPI handles dependency injection internally:

  • Type Hints and Annotations: FastAPI heavily relies on Python’s type hinting system. When you define a route handler, you can use type hints for function arguments and annotations for function return types.
  • Function Inspection: FastAPI inspects the annotations and types of function parameters using Python’s inspect module. This inspection allows FastAPI to understand the dependencies required by each route handler.
  • Dependency Resolution: When a request is received, FastAPI uses the information obtained from function annotations to identify dependencies. For each dependency required by the route handler, FastAPI calls the corresponding dependency function to resolve the dependency.
  • Injection into Route Handlers: FastAPI automatically injects resolved dependencies into route handlers. When a request matches a route, FastAPI calls the route handler function and passes the resolved dependencies as arguments.
  • Optional Dependencies: Dependencies can be optional, meaning that they are not required for every request. If a dependency is not needed for a specific route, you can omit it, and FastAPI will not try to resolve it. This flexibility allows you to tailor the dependencies for different routes.
  • Lifecycle Management: FastAPI provides hooks for managing the lifecycle of dependencies. For instance, you can create a dependency with a scope (e.g., Depends(get_db, scope=”request”)) to ensure that the dependency is created once per request, or you can use other scopes like “singleton” or “application” based on your use case.
  • Error Handling: FastAPI handles errors related to dependency resolution, ensuring that appropriate error responses are generated when a required dependency cannot be resolved.

Response Generation

FastAPI generates responses by utilizing Python’s type hinting system, Pydantic models, and the JSON serialization capabilities of the underlying Starlette framework. The response generation process in FastAPI involves the following steps:

  • Type Hints and Annotations: When you define a route handler in FastAPI, you can specify the expected response model using Python’s type hinting system.
  • JSON Serialization: FastAPI uses Pydantic models and the jsonable_encoder function from the fastapi.encoders module to serialize Python objects (including Pydantic models) into JSON. This serialized JSON data forms the response body.
  • Response Model Validation: Before sending the response, FastAPI validates the response data against the specified response model. If the response data does not conform to the model, FastAPI automatically generates an error response with appropriate status code and error details.
  • Status Codes and Headers: FastAPI automatically handles setting the appropriate status code (usually 200 OK for successful responses) and headers for the response. It also handles CORS (Cross-Origin Resource Sharing) headers when necessary.
  • Dependency Injection and Background Tasks: FastAPI’s dependency injection system can also be used to manage responses. For example, you can create a dependency that handles response headers or background tasks, allowing for further customization of the response generation process.
  • Custom Responses: If you need to return a custom response with a specific status code, headers, or other attributes, you can directly return a JSONResponse or a PlainTextResponse from a route handler.
  • Automatic Documentation: FastAPI uses the provided response models and type hints to generate interactive API documentation. This documentation includes details about the expected response format, status codes, and example responses.

Internally, FastAPI leverages the response generation capabilities provided by the Starlette framework, on which FastAPI is built. Starlette handles the low-level HTTP handling and response generation, while FastAPI enriches this process by providing high-level abstractions, automatic validation, and Pydantic model integration, making it easier for developers to work with responses in a strongly typed and intuitive manner.

Middleware

In FastAPI, middlewares are implemented and managed using the underlying Starlette framework, which provides a flexible and powerful middleware system. FastAPI builds upon Starlette’s middleware capabilities to allow developers to define custom middleware functions and apply them to specific routes or the entire application. Here is the overview of middlewares in FastAPI and Starlette:

  • Starlette Middleware Handling: Starlette handles the execution of middlewares internally. When a request hits the FastAPI application, it passes through all registered middlewares in the order they are defined. Each middleware’s call_next function invokes the subsequent middleware or the route handler. Finally, the response travels back through the middlewares in reverse order before being sent to the client.
  • Exception Handling: Middlewares are also involved in exception handling. If an exception is raised during the request handling process (in a middleware or route handler), the middlewares are called in reverse order, allowing for exception handling and customization of error responses.
  • Context Passing: Middlewares share the same context and state as the request and response objects. This allows middlewares to modify or inspect the request and response components.
  • Background Tasks and Dependencies: Middlewares can execute background tasks and use dependencies. They can declare and use dependencies, just like route handlers, providing flexibility and reusability.

While the internal implementation details of middlewares are handled by the Starlette framework, FastAPI offers a clean and intuitive interface for developers to define, manage, and apply middlewares to their applications, enhancing the application’s functionality and customization capabilities.

FastAPI’s core components make it a reliable framework, providing developers with a robust, efficient, and intuitive solution for building APIs with Python. Its architecture promotes clean and readable code while managing complex tasks such as data validation, serialization, and dependency management under the hood.



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads