FastAPI Tutorial (Part 1) - The Basics

FastAPI Tutorial (Part 1) - The Basics

Summary: ↬ Let's learn how to build an API using Python framework FastAPI, by creating a online book store ecommerce service.

Mar 25, 2022

13 min read

---


Introduction

Welcome to the FastAPI tutorial series. This is part 1 of a series of posts where I aim to show how to build a Book store API using the Python framework FastAPI.

This series is aimed to beginners using FastAPI, but if you have previous knowledge you can contribute to it too. Each post will build on the previous, however, I did my best to divide it in a way that if you know about FastAPI and are only interested in one part you can jump to it directly.

I think FastAPI's docs are really good and you can learn to use the framework on your own just by checking on them. That said, I think there are few things that could be explained better for beginners and that's one of the reasons I decided to create this series.

Tutorial Series Contents

Disclaimer

As with most of my guides, I do not claim to be an expert in the topic. I however, like sharing what I learn, and also, I'm a firm believer that you learn more when you teach or share what you know. That said, I do take the time and effort to do a lot of research on the topic in order to provide valid and updated info that can be useful for others, specially for beginners.

In any case, if you find an error in the information, or something you thing could be explained or approached in a better way, I would appreciate you letting me know so I can fix any mistake or even encourage you to make a PR on the project main repo. That way, we can all work together to provide the best information possible to others.

Requirements

Although this is a series aimed for beginners, it's important to have a basic knowledge on at least the following topics:

  • Python programming language (variables, dictionaries, lists, functions, modules)
  • API's (HTTP methods, endpoints)
  • Back-end development

For this tutorial series, I'm using the following tools and versions:

  • Python: 3.10.2
  • FastAPI: 0.75.0
  • Uvicorn: 0.17.6

I'm also using Poetry as my dependency manager, but that's just a personal preference and is not required to follow along. If you prefer, you can use Pip to manage dependencies.

Project files and repo

I've created a Github repository to storage all the project files. You can check it here. Each part of the tutorial (post) has a branch so you can check the code for each post. Inside the main you'll see the updated code (the final version).

There is also a starter branch with the starter files if you're following along.

What we're building

By the end of the tutorial, we'll have a complete API for a fictional online book store. Users will be able to see all the available books, single book details, authors, register, login/logout, make orders.

The API will handle database integration, user register, handle request errors and expose a fully documented API by taking advantage of Swagger-UI capabilities.

What is FastAPI?

If you're reading this, chances are, you already know what FastAPI is. But just in case you don't know or haven't seen the documentation yet, let's see a basic definition for it.

FastAPI is a fast, lightweight Python framework to build, well, API's. Think of it like something similar to Flask or Django. That said, FastAPI offer some key features that differentiate it from other python frameworks, like, fast and enjoyable development experience, high-performance, minimalistic and much more. You can read more on FastAPI official docs.

Project Setup

To start with our project, first, let's create our development setup. If you're following along, there are two ways you can get started. You can either use the starter code by cloning or downloading the starter Github repo (recommended). Or if you prefer, you can get started by creating the project from scratch.

Let's see both alternatives next.

Using starter template

If you downloaded/cloned the starter project you can find a main.py file inside with the following content:

main.py
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def index():
	return {"message": "Hello World"}

Before running the local development server, make sure to create a virtual environment for your application and install the dependencies. After creating your environment, activate it and install the dependencies. If you're using Poetry you can run poetry install, alternatively, you have a requirements.txt inside your project folder from which you can install the dependencies.

To install dependencies with pip, run:

$ pip3 install -r requirements.txt

After installing dependencies, you can run the local server using the following command in your terminal:

$ uvicorn app.main:app --reload

In your browser, go to http://localhost:8000, if you see the following result, you're good to go.

server response message

You can skip the next section and go to Quick Recap.

Create basic project from scratch

If you want to create the project from scratch, just follow this instructions:

  • Create a new folder and name it fastapi-tutorial or use the name you prefer.
  • Inside the folder we will create a dependency manager file. If you want to use pip create a file named requirements.txt. Alternatively you can use Poetry to manage dependencies by typing in the terminal poetry init and follow the instructions (you have to install poetry first), when you're prompted to define your dependencies you can select no, we will define them in the next step. Once you finish your poetry setup a new pyproject.toml file will be created in your project directory root.
  • Before installing dependencies remember to create your virtual environment. If you're using pip you can create your environment by typing the following in your terminal:
$ python3 -m venv venv

# Activate the environment
$ source venv/bin/activate # for mac users
$ venv\Scripts\activate.bat # for windows users

If your using Poetry you can create a new environment by typing on your terminal:

$ poetry shell

This will create a new environment or activate it if there is one already. You can read more about python virtual environments here. Remember in your text editor to select this virtual environment as your python interpreter.

In any case, once you have activated your virtual environment we can now install our dependencies.

  • Now that we have an active virtual env, let's install project dependencies.
$ pip3 install fastapi
$ pip3 install uvicorn

# or if using poetry
$ poetry add fastapi uvicorn

I'm also using the black library as a code formatter, this is an optional step but if you want you can install it the same way as the other dependencies (if you're using Poetry you can install black as a dev dependency by adding the flag --dev before the package name).

Finally, in your root directory create a folder called app (we will talk about why doing it this way later), inside this folder create this two files: __init__.py and a main.py file and add the following code to the main.py file and run it.

main.py
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def index():
	return {"message": "Hello World"}

Run the server like this:

$ uvicorn app.main:app --reload

If everything went ok, weather you created the project from scratch or copied it from Github, you should see the following result of running your server by navigating in your browser to http://localhost:8000:

server response message

We should end up with the following folder structure.

fastapi-tutorial/
├── .vscode/ --> Optional: Only if you have any project specific setting
├── app/
│ ├── **init**.py
│ └── main.py
├── .gitignore
├── main.py
└── pyproject.toml

If you're not using poetry, you should have a file named requirements.txt instead of pyproject.toml.

Also, if you're using poetry the default config will create your venv folder in a location different from your project root. You can change this behavior by typing the following command in your terminal:

$ poetry config virtualenvs.in-project true

Quick Recap

So far, we're just getting started with our setup, but, before starting with the main project, let's quickly recap what we've done up to this point.

  1. First, we created a virtual environment for our application. We did this with either using poetry or with simple python/pip.
  2. Then we install the main project dependencies: FastAPI and Uvicorn. Later we will install other dependencies, but, for the moment this is enough.
  3. Finally we create a main.py file inside our project's root. We end up with the following:
main.py
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def index():
	return {"message": "Hello World"}

I'm not going to go into too much detail about this code, because I think the docs do a really good job explaining line by line. But basically first, we import the FastAPI class, then create an instance of the class and we call it app. Finally we create a function called index (you can use the name you prefer for this), this function is using a python decorator with the app instance we created which will allow us to use this function to return something to the browser (create an endpoint). In this case we're returning a dictionary with a message key and a value of Hello World.

If we run our uvicorn server, we see the following result on the default route (http://localhost:8000):

$ uvicorn app.main:app --reload
server response message

By using the --reload flag in the uvicorn command we're telling uvicorn to watch our project for any change and reload the server automatically.

Let's start our project API

As I mentioned before we were just getting our setup ready. I wanted to take some time in this part to make sure everybody is starting from the same place.

Now, let's start our main API.

Project requirements in details

First, let's define our project requirements so it is easier to understand our end goal.

The client application should be able to perform all CRUD operations for the books stored in the database (Create, Read, Update, Delete).

Also, the client should be able to authenticate any user, and users should be able to register, login, logout, reset password, etc.

We're going to start working with our books endpoints. For this, let's add two new folders to our application.

fastapi-tutorial/
├── .vscode/
├── app/
│ ├── endpoints/ --> NEW
│ │ ├── **init**.py
│ │ ├── api.py
│ │ └── books.py
│ ├── **init**.py
│ └── main.py
├── .gitignore
├── main.py
└── pyproject.toml

First, let's work with the endpoints folder, inside it, let's create the 3 files you see in the project tree __init__.py, api.py and books.py.

Remember that by adding a __init__.py file to our folders, python will see this folders as a module, which will make easier to import from them.

FastAPI Router

FastAPI allows us to create separated routers for our application, that way it's easier to separate our project in different files. For example, for our application, we'll have routes for books, users, authors; now, instead of creating 1 file for all our endpoints, with FastAPI router we can create 1 file for each part of our routing system (1 file for books, 1 for users, and so on...). To do this, inside our books.py file:

books.py
from fastapi import APIRouter

# Instanciate a new fastapi router
router = APIRouter()

# Add mock data
books = [
	{"id": 1, "title": "book 1", "price": 16.99},
	{"id": 2, "title": "book 2", "price": 12.99},
	{"id": 3, "title": "book 3", "price": 9.99},
]

The router variable has all the properties from the APIRouter class. This will allow us to create new decorators for each of the required HTTP methods (GET, POST, PUT, DELETE). Having this in mind, let's create a new route for our required methods.

books.py
# code above omitted 👆

@router.get("/")
def get_books():
	"""Get books from database"""

	return books

The first method will be a GET method to retrieve books from our API. Now before continuing, let's connect our books router to the main application and test it.

Connect router

To connect our router, let's go to the api.py file created in the same level as our books.py file. Inside we copy the following code.

api.py
from fastapi import APIRouter

from . import books

api_router = APIRouter()

api_router.include_router(books.router, prefix="/books", tags=["Books"])

Let's review the code:

  • First, we create a new APIRouter instance called api_router. This will be the main API router.
  • Next, we add the newly created books router to our api_routerusing the function include_router and pass the books.router as the first parameter.
  • The prefix param allows to tell the router to base route for this particular router, in our case, since we're working with books we use /books as our prefix,
  • The tags params helps to identify this routes in our documentation.

Finally, in our main.py we import this new router.

main.py
# from fastapi import FastAPI

from app.endpoints.api import api_router

# app = FastAPI()

app.include_router(api_router, prefix="/api")

# @app.get("/")
# def index():
#	return {"message": "Hello World"}

We included our main router the same way as with the books router.

By using the prefix="/api" we're telling python that all our endpoints will start with the /api prefix.

This means that to hit our books endpoints, we'll have a full Url similar to something like this:

http://my-domain.com/api/books

FastAPI - API Docs

Normally, when we create a new API, we use something like Postman to test our endpoints. One of the many benefits of using of using FastAPI is that the framework makes use of the Swagger-UI library to create our API documentation as we develop it.

To make use of the docs, all we have to do is, in our browser go to http://localhost:8000/docs. You should see something like this:

server response for base route

You can see, we already have two new endpoints, one GET for the path /api/books/ and one GET for our default index route created in the main.py file.

You can expand this routes and will see something like this:

server response details in docs

You can try this endpoint by clicking on the Try it out button and then click the Execute button. You should get a response like this:

get books response object

You notice, in the response body we get exactly the same 3 dummy books we created on our books.py file.

You can use this docs to test your entire API. Later we'll add schemas that will allow us the validate the data we receive and send from our API.

Alternatevily, instead of Swagger docs, you can go to htpp://localhost:8000/redoc to see other type of documentation that also will allow you to test your API.

For the purpuse of this tutorial series, we'll use Swagger docs, but feel free to use any API tester you prefer.

Next steps

At this point we created our API setup, and added a basic endpoint to retrieve all books available in our database.

In the next part of the tutorial, we'll focus on the rest of the endpoints for our books store. We will see how to get URL parameters, parse the Request body to create new books and add them to our dummy list.