FastAPI — a relatively new web framework written in the Python programming language for creating a REST (and if you try really hard, then GraphQL) API, based on new features of Python 3.6+, such as: type-hints, native synchronicity (asyncio). Among other things, FastAPI tightly integrates with OpenAPI-schema and automatically generates documentation for your API via Swagger and ReDoc.
FastAPI is based on Starlette and Pydantic.
Starlette — an ASGI micro framework for writing web applications.
Pydantic — a library for parsing and validating data based on Python type-hints.
What do people say about FastAPI?
“[…] I’m using fastapi a ton these days. […] I’m actually planning to use it for all of my team’s ML services at Microsoft. Some of them are getting integrated into the core Windows product and some Office products.”
Kabir Khan — Microsoft (ref)
“If you’re looking to learn one modern framework for building REST APIs, check out FastAPI. […] It’s fast, easy to use and easy to learn. […]”
“We’ve switched over to FastAPI for our APIs […] I think you’ll like it […]”
Ines Montani — Matthew Honnibal — Explosion AI founders — spaCy creators (ref) — (ref)
Minimal API created with FastAPI
I will try to show you how to create a simple but useful API with documentation for developers. We will write a random phrase generator!
Installation of necessary components
pip install wheel -U
pip install uvicorn fastapi pydantic
New module!
Uvicorn — this is an ASGI-compatible web server that we will use to run our application.
First, let’s create the basis of our application.
from fastapi import FastAPI
app = FastAPI(title="Random phrase")
This app already works and can be started.
Run the following command in your terminal and open the page in the browser at the address http://127.0.0.1:8000/docs.
uvicorn <your filename>:app
But so far, our app doesn’t have any endpoints — let’s fix that!
Database
Since we’re writing a random phrase generator, we obviously have to store the phrases somewhere. For that, I chose a simple python-dict.
Let’s create the file db.py and start writing code.
Import the necessary modules:
import typing
import random
from pydantic import BaseModel
from pydantic import Field
Then – we will designate two models: the input phrase (the one that the user will send to us) and the “output” (the one that we will send to the user).
class PhraseInput(BaseModel):
"""Phrase model"""
author: str = "Anonymous" # author name. If not passed, the standard value is used.
text: str = Field(..., title="Text", description="Text of phrase", max_length=200) # The text of the phrase. The maximum value is 200 characters.
class PhraseOutput(PhraseInput):
id: typing.Optional[int] = None # ID of phrases in our database.
After that, we will create a simple class to work with the database:
class Database:
"""
Our **fake** database.
"""
def __init__(self):
self._items: typing.Dict[int, PhraseOutput] = {} # id: model
def get_random(self) -> int:
# Getting a random phrase id
return random.choice(self._items.keys())
def get(self, id: int) -> typing.Optional[PhraseOutput]:
# Getting a phrase by id
return self._items.get(id)
def add(self, phrase: PhraseInput) -> PhraseOutput:
# Adding a phrase
id = len(self._items) + 1
phrase_out = PhraseOutput(id=id, **phrase.dict())
self._items[phrase_out.id] = phrase_out
return phrase_out
def delete(self, id: int) -> typing.Union[typing.NoReturn, None]:
# Deleting a phrase
if id in self._items:
del self._items[id]
else:
raise ValueError("Phrase doesn't exist")
Now we can start writing the API itself.
API
Let’s create the file main.py and import the following modules:
from fastapi import FastAPI
from fastapi import HTTPException
from db import PhraseInput
from db import PhraseOutput
from db import Database
Initialize our application and database:
app = FastAPI(title="Random phrase")
db = Database()
And let’s write a simple method for getting a random phrase!
@app.get(
"/get",
response_description="Random phrase",
description="Get random phrase from database",
response_model=PhraseOutput,
)
async def get():
try:
phrase = db.get(db.get_random())
except IndexError:
raise HTTPException(404, "Phrase list is empty")
return phrase
As you can see, I also specify some other values in the decorator to generate pretty documentation 🙂 You can look at all the possible parameters in the official documentation.
In this piece of code, we try to get a random phrase from the database, and if the database is empty, we return an error with the code 404.
Similarly, we write other methods:
@app.post(
"/add",
response_description="Added phrase with *id* parameter",
response_model=PhraseOutput,
)
async def add(phrase: PhraseInput):
phrase_out = db.add(phrase)
return phrase_out
@app.delete("/delete", response_description="Result of deletion")
async def delete(id: int):
try:
db.delete(id)
except ValueError as e:
raise HTTPException(404, str(e))
That’s all! Our small but useful API is ready!
Now we can launch the app using uvicorn, open the online documentation (http://127.0.0.1/docs), and try our API!
Useful material
Of course, I couldn’t tell you about all the features of FastAPI, such as: smart DI system, middlewares, cookies, standard authentication methods in the API (jwt, oauth2, api-key) and much more!
But the purpose of this article is not so much to review all the features of this framework, but rather to encourage you to explore it yourself. FastAPI has excellent documentation with a bunch of examples.
Code from the Github article
Official documentation
Repository on Github
Written by prostomarkeloff, translated from here
For additional information check out this tutorial on how to build a high performing app in FastAPI from Toptal.