While Flask was created as an April fool's joke in 2004, and Django was an internal project at Lawrence Journal-World newspaper around the same time, both these Python web frameworks gained massive prominence and popularity.
However, the Python of when these frameworks were built is different from the Python of today. Contemporary elements of the language, such as the ASGI standard and the asynchronous execution, were either not trendy or didn't exist.
And this is where FastAPI comes in.
It is a newer Python web framework built in 2018 to incorporate Python's modern features. Besides being capable of using the ASGI standard for asynchronous connectivity with clients, it can also work with WSGI.
Furthermore, FastAPI web applications are written in clean code with type hints, and the async functions can be used for routes and endpoints.
As the framework's name implies, one of the main uses of FastAPI is building API endpoints. With the framework, building the endpoints is as simple as returning a Python dictionary's data as JSON. Alternatively, you can use the OpenAPI standard, which includes an interactive Swagger UI.
But FastAPI is not limited to APIs and can be used to accomplish any task that a web framework can accomplish – from delivering webpages to serving applications.
In this post, we discuss how to install FastAPI and the fundamentals of using it.
What is Fast API? What Makes It Stand Out from Other Frameworks?
FastAPI is a high-performance web framework that facilitates building Python APIs using standard type hints. Its key features include:
- Easy to learn: The framework's uncomplicated nature minimizes the time spent reading the documentation.
- Intuitive: FastAPI offers excellent editor support and completes your code, reducing the time you need to invest in debugging.
- Reduces bugs: Since it's quite straightforward to use, the chances of developers making mistakes are lower.
- Robust: It facilitates production-ready code and automatic interactive documentation.
- Speed: Boasting extreme performance, the framework is comparable to NodeJS and Go. Furthermore, it enables rapid development.
- Standards-based: It is based on open API standards such as JSON Schema and OpenAPI.
In addition to all this, it minimizes code duplication. Overall, the framework optimizes the developer's experience and enables them to code effectively by enforcing the best practices by default.
How to Install FastAPI
Starting a FastAPI project on a new virtual environment is recommended since the framework installs many components without prompting. To install the core components of the framework, you can run the following command:
pip install fastapi
Besides this, you will need to install an ASGI server to perform local tests. The framework works seamlessly with Uvicorn. Therefore, we will use that server in our examples.
To install Uvicorn, you can run the command:
pip install uvicorn[standard]
The above command installs an optimal set of components, including C libraries. If you want to install a minimal version that only uses Python, you can run:
pip install uvicorn
Basic Example of a FastAPI App
Here's what a basic FastAPI app looks like:
from fastapi import FastAPI app = FastAPI() @app.get("/") async def root(): return {"greeting":"Hello coders"}
To run this app, you can save the code in a text file and name it main.py, then use the following command to run it from within your virtual environment:
uvicorn main:app
While the "uvicorn" part of the above command is self-explanatory, the "app" is an object which you would use for your ASGI server. If you want to use WSGI, you can use an ASGI-to-WSGI adapter. However, it's best to use ASGI.
When the app begins running, go to localhost:8000, the default Uvicorn test server. You will see the ("greeting":" Hello coders") displayed on your browser, and this is a valid JSON response that the dictionary generated.
You can now discern how easy it is to use FastAPI to deliver JSON from an endpoint. The idea is clear – you have to create a route and return a Python dictionary, which will be serialized into JSON without needing you to intervene.
That said, serializing tricky data types will take some work, and we will discuss this in more detail later in the post.
If you have previously worked with systems like Flask, it will be easy for you to recognize the general outline of a FastAPI app:
- The ASGI/WSGI server imports the app object and uses it to run the app.
- You can use a decorator to add routes to an app. For example, the @app.get("/") decorator makes a GET method route at the website's root, and the wrapped function returns the results.
But some differences will stand out to someone familiar with the workings of frameworks. Firstly, the route functions are asynchronous, meaning any async components deployed – like an async database middleware connection – can run in the functions.
It's important to note that you can use regular sync functions if you want to. If you have an operation that uses a lot of computational power rather than one that waits on the I/O (which is the best use case for async), it's best to use a sync function and let FastAPI do the legwork for you.
In other cases, using async functions is the right way to go.
Route Types in FastAPI
Using the @app decorator, you can set the method for the route. You could do this using @app.get or @app.post. The POST, GET, DELETE and PUT methods are used this way. The lesser-known functions HEAD, TRACE, PATCH, and OPTIONS are also used this way.
It is also possible to use multiple methods on a route by wrapping the route functions together by using @app.get("/") on one function and @app.post("/") on the other.
The Main Parameters in FastAPI
Extracting variables from a route's path is as simple as defining them in the route declaration before passing them to the route function, like so:
@app.get("/users/{user_id}") async def user(user_id: str): return {"user_id":user_id}
Next, you can extract the query parameters from the URL with typed declarations in the route function. The framework will automatically detect these declarations:
userlist = ["Spike","Jet","Ed","Faye","Ein"] @app.get("/userlist") async def userlist_(start: int = 0, limit: int = 10): return userlist[start:start+limit]
Working with form data is comparatively more complicated. You will need to manually install the Python multipart library to parse the form data. When you have the library installed, you can use a syntax similar to the query parameter syntax:
from fastapi import Form @app.post("/lookup") async def userlookup(username: str = Form(...), user_id: str = Form("")): return {"username": username, "user_id":user_id}
The Form object in the code above pulls the named parameter from the form and passes it forward. You must remember that using the Form(…) in the declaration means that the respective parameter is mandatory to enter, such as the username in this case.
On the other hand, you can pass the default value for the element into Form if there is an optional form element such as user_id in the example above.
FastAPI Response Types
JSON is the default response type in FastAPI, and all the examples you in this post serialize the data as JSON automatically. However, you can also return other responses. For instance:
from fastapi.responses import HTMLResponse @app.get("/") def root(): return HTMLResponse("<b>Hello world</b>")
The fastapi.responses module is among the most useful modules of FastAPI, and it supports a large array of responses, including:
- FileResponse: Returns a file from a specific path, streamed asynchronously.
- PlainTextResponse or HTMLResponse: Text is returned as plain text or HTML.
- RedirectResponse: Redirects to a specific URL.
- StreamingResponse: Accepts a generator as input and streams the results to the client.
Rather than using the responses listed above, you can use a generic response object and offer custom headers, media type, status code, and content.
FastAPI's Response Object
Whether you're setting cookies or headers to work with a response, you will need to accept a Response object as a parameter to the route function:
from fastapi import Response @app.get("/") def modify_header(response:Response): response.headers["X-New-Header"] = "Hi, I'm a new header! return {"msg":"Headers modified in response"}
Cookies in FastAPI
Retrieving cookies from the client works similar to how handling form parameters or queries works:
from fastapi import Cookie @app.get("/") async def main(user_X: Optional[str]=Cookie(none)): return {"user_X": user_X}
You can set cookies with the .set_cookie() method on a Response object:
from fastapi import Response @app.post("/") async def main(response: Response): response.set_cookie(key="user_X", value="") return {"msg":"User X cookie cleared"}
Pydantic Models and FastAPI
While Python types are optional, FastAPI insists on using types, unlike most other Python frameworks. This framework utilizes the Pydantic library to verify submitted data, removing the need for you to write logic to accomplish verification.
Let's take a closer look at the Pydantic library with some code that validates incoming JSON:
from typing import List, Optional from fastapi import FastAPI from pydantic import BaseModel, EmailStr app = FastAPI() class Movie(BaseModel): name: str year: int rating: Optional[int] = None tags: List[str] = [] @app.post("/movies/", response_model=Movie async def create_movie(movie: Movie): return movie
The above code accepts JSON data over POST and not HTML via the name, year, rating, and tags fields. Next, the types of the fields are validated.
OpenAPI in FastAPI
OpenAPI is a JSON-formatted standard for creating API endpoints. Clients can read the OpenAPI definition for endpoints and automatically identify the schemas for the data that the site's API transfers.
If you use FastAPI, it will generate the required OpenAPI definitions for the endpoints automatically. For instance, if you visit /openapi.json at the root of an OpenAPI site, you will receive a JSON file that describes every endpoint and the data it can receive and return.
Another benefit of FastAPI is that it automatically creates documentation interfaces for your APIs. You can interact with them using a web interface. Additionally, the framework comes with hooks that allow you to extend or modify the auto-generated schema. It also allows you to conditionally generate the schema or disable it entirely.