How to Build a Python API from Scratch with FastAPI

Daniel Adeboye - Sep 5 - - Dev Community

Python has always been my go-to when building APIs for any of my projects, and in this tutorial, we’re going to build a simple blog application API using FastAPI. We’ll start with structuring our FastAPI project, installing the necessary dependencies, creating our application model structure, writing the functionalities for our application, and finally testing out our API.

💡 If you are interested in building something similar using Python Flask, you can check out my 1-hour-long video tutorial on Youtube.

Prerequisites

To follow this tutorial, you should have:

  1. Basic knowledge of Python
  2. Basic knowledge of API and how it works.

These are the steps that we will cover:

  • Step 1 - Project Setup
  • Step 2 - Running our first basic application
  • Step 3 - Creating our Application Model Structure
  • Step 4 - Writing our Application CRUD functionalities
  • Step 5 - Testing our API endpoints
  • Step 6 - Exercise

Step 1 - Project Setup

In this section we will focus on setting up our project, starting with folder creation, environment setup, and installing necessary dependencies.

Folder Creation

On your terminal create a project directory with any name of your choice. In my case I will call my project “anvil-blog”. You can also do this using your computer’s file explorer user interface.



$ mkdir anvil-blog


Enter fullscreen mode Exit fullscreen mode

After creating your application folder, you will need to change your directory into “anvil-blog”.



$ cd anvil-blog


Enter fullscreen mode Exit fullscreen mode

Environment Setup

Inside your project directory, we will create and activate a virtual environment, which will house all of our application dependencies. In this case, I called it “env”.



$ python -m venv env
$ env/bin/activate


Enter fullscreen mode Exit fullscreen mode

Installing Dependencies

To build our API using FastAPI, we need to install necessary dependencies that will help our application run smoothly, and below are what we need:

  • FastAPI - A web framework for building APIs with Python.
  • Uvicorn - An ASGI web server implementation for Python
  • Pydantic - A data validation library for Python.

Run the command below to install these packages:




(env)$ pip install fastapi uvicorn pydantic


Enter fullscreen mode Exit fullscreen mode

Step 2 - Running our first basic application

Application Setup

Now that we have our project environment setup, it’s time to write some FastAPI code. Create a new file in your project folder and name it “main.py”, this is where we’ll write all of our application code.

Let’s add this code to our “main.py” file.



import uvicorn
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def anvil():
    return {"hello": "world"}

if __name__ == "__main__":

    uvicorn.run(app, host="0.0.0.0", port=8000)


Enter fullscreen mode Exit fullscreen mode

Let’s break down the above code:

  1. @app.get("/") defines a route for our FastAPI application. When someone accesses the root URL (/), the anvil function is called. This function simply returns a JSON response with the message "hello": "world".
  2. if __name__ == "__main__": ensures that our code runs only when the script is executed directly. Inside this block, uvicorn.run is called to start the Uvicorn server, which serves our FastAPI app at the specified host (0.0.0.0) and port (8000).

Running our Application Server

To run our application server run the command below in your terminal:



(env)$ python main.py 


Enter fullscreen mode Exit fullscreen mode

You should get similar result as the image below:

Image Server

You can now access your application at http://localhost:8000/.

Image Application

🎉 Congratulations, we have successfully run our first FastAPI application.

Step 3 - Creating our Application Model Structure ****

Earlier we installed Pydantic, a library that helps us validate and serialize data schemas, we’ll use this library to create our application model structure.

Let’s add this code to our “main.py” file:



from pydantic import BaseModel
from typing import List, Optional
from uuid import UUID, uuid4

class Blog(BaseModel):
    id: Optional[UUID] = None
    title: str
    description: str


Enter fullscreen mode Exit fullscreen mode

Now, let's break down the code:

  1. We've used UUID also known as universally unique identifier, to ensure that each Blog instance has a unique identifier. This helps us guarantee that the id for each blog post is always unique.
  2. The Optional type indicates that the id field is not required when a new blog post is created. If the id is not provided, it defaults to None, allowing the system to generate it automatically.

Creating a Temporary Database for our Model

Because we are not using a real database in this tutorial, we are going to use a list instead. Let’s add the code below under our Blog model:



blogs = []


Enter fullscreen mode Exit fullscreen mode

Here is what your “main.py” file should look like:



import uvicorn
from fastapi import FastAPI
from pydantic import BaseModel
from typing import List, Optional
from uuid import UUID, uuid4

app = FastAPI()

class Blog(BaseModel):
    id: Optional[UUID] = None
    title: str
    description: str

blogs = []

if __name__ == "__main__":

    uvicorn.run(app, host="0.0.0.0", port=8000)


Enter fullscreen mode Exit fullscreen mode

Step - 4 Writing our Application CRUD functionalities

You might be curious about what CRUD stands for. It represents the four main operations commonly performed in most applications: Create, Read, Update, and Delete.

Create Blog Post API

In the Create Blog Post API, we're going to allow users to create blog posts. Here's how it works:



@app.post("/blogs/", response_model=Blog)
def create_blog(blog: Blog):
    blog.id = uuid4()
    blogs.append(blog)
    return blog


Enter fullscreen mode Exit fullscreen mode

Now, let's break down the code:

  • response_model=Blog tells FastAPI to use the Blog model to structure the JSON response returned from the /blogs/ route.
  • (blog: Blog) specifies that we want to accept a new blog post using our Blog model.
  • blogs.append(blog) as you might already know adds the new blog post to our temporary database, which is represented by the blogs[].

Get Blog List API

In the Get Blog List API, we're going to allow users to retrieve all the blog posts they have created. Here's how it works:



@app.get("/blogs/", response_model=list[Blog])
def list_blogs():
    return blogs


Enter fullscreen mode Exit fullscreen mode

Now, let's break down the code:

  • response_model=list[Blog] as you might already know tells FastAPI that the response will be a list of Blog objects.
  • return blogs: This line returns the entire list of blog posts stored in our temporary database, which is the blogs[].

Update Blog Post API

In the Update Blog Post API, we allow users to modify an existing blog post by providing its unique identifier (UUID) and the new data they wish to update. Here’s how it works:



@app.put("/blog/{blog_id}", response_model=Blog)
def update_blog(blog_id: UUID, blog_update: Blog):
    for idx, blog in enumerate(blogs):
        if blog.id == blog_id:
            updated_blog = blog.copy(update=blog_update.dict(exclude_unset=True))
            blogs[idx] = updated_blog
            return updated_blog

    raise HTTPException(status_code=404, detail="Blog not found")


Enter fullscreen mode Exit fullscreen mode

The above code looks a bit more complex than the previous ones we've written, so let’s break it down:

  • @app.put("/blogs/{blog_id}") route makes the update function accessible. The {blog_id} part is a path parameter that takes the unique identifier of the blog post you want to update.
  • blog_update: Blog: This parameter indicates that the request body should contain a Blog model with the new data to be applied.
  • The update_blog function iterates through the blogs list and checks for a blog post with a matching blog_id. When it finds a match, it updates that entry using the copy(update=blog_update.dict(exclude_unset=True)) method. This method updates only the fields provided in the request, leaving the others unchanged.
  • If the blog post is found and updated, the function returns the updated blog. If not, it raises an HTTPException with a 404 status code, indicating that the blog post was not found.

Delete Blog Post API

For this functionality, we want to allow users to remove a blog post by providing its unique identifier (UUID).

Let’s add this code to our "main.py" file:



@app.delete("/blogs/{blog_id}", response_model=Blog)
def delete_blog(blog_id: UUID):
    for idx, blog in enumerate(blogs):
        if blog.id == blog_id:
            return blogs.pop(idx)

    raise HTTPException(status_code=404, detail="Blog not found")


Enter fullscreen mode Exit fullscreen mode

Let’s break down the above code:

  • @app.delete("/blogs/{blog_id}") as you might already know, this route provides access to the delete function. Similar to the update route, {blog_id} is a path parameter that takes the unique identifier of the blog post you want to delete.
  • The delete_blog function iterates through the blogs list, and if it finds a blog post with a matching blog_id, it removes that post from the list using pop(idx) and returns the deleted blog.
  • If the blog post is not found, the function raises an HTTPException with a 404 status code, indicating that the blog post was not found.

💡 You can access the entire code for this tutorial on GitHub.

Step 5 - Testing our APIs

To test our application APIs, start your FastAPI project server using the command below in your terminal:



(env)$ python main.py 


Enter fullscreen mode Exit fullscreen mode

FastAPI, by default, automatically generates API documentation without any extra setup. To access your project’s API documentation, head over to http://localhost:8000/docs#/ .

Image API

You can also test your API endpoints using the API documentation.

Step 6 - Exercise

Write an API that gets individual blogs via their id. Submit your answer as a pull request on this repository. I will give you feedback in 1-2 weeks. If you want a faster review, you can tag me on X with the URL to your pull request.

Conclusion

In this article, we learned how to build a Python API using FastAPI. We covered setting up a FastAPI project, defining the project model structure with Pydantic, implementing CRUD functionalities for our blog, and testing our APIs.

. . . .