Abdulazeez Abdulazeez Adeshina Software enthusiast, writer, food lover, and hacker.

Deploying FastAPI applications to Vercel

5 min read 1648

Deploying FastAPI Applications to Vercel

Introduction

Deploying applications to a web or cloud hosting platform is usually the last step in the development cycle, allowing users to access our apps at last. While there are many tools to make this happen, in this article, we will be learning how to deploy FastAPI applications to Vercel.

FastAPI is a modern and fast Python web framework for building backend API applications. FastAPI comes with support for API documentation powered by Swagger, security modules, and type checking to ensure correctness in code.

Prerequisites

What we will build

To demonstrate how FastAPI applications are deployed to Vercel, we will be building a simple notes app.

From this point, I’ll assume you have Python and Virtualenv installed. Check by running the commands below:

$ python3 --version

Then run:

$ virtualenv --version

Setup

Before we dive too deep, let’s map out the project structure and the installation of the dependencies needed for your application. Start by creating the project folder:

$ mkdir fastapi-notes-app && cd fastapi-notes-app
$ mkdir server
$ touch {main,server/api,server/routes,server/__init__}.py

Next, create a virtual environment in the base directory and install the dependencies needed:

$ virtualenv -p python3.8 venv

Next, we’ll activate the virtual environment, an isolated part of our application where we will install the dependencies for our app. To do so, run the command below:

$ source venv/bin/activate

With the virtual environment in place, install FastAPI and Uvicorn:

(venv)$ pip3 install fastapi uvicorn

Uvicorn is an ASGI (Asynchronous Server Gateway Interface) server that enables us to run our application.

We made a custom demo for .
No really. Click here to check it out.

Now, let’s create a base route to verify that the installation of FastAPI and Uvicorn was successful.

server/api.py

Start out by importing FastAPI and initializing the class method into a variable, app:

from fastapi import FastAPI

app = FastAPI()

Next, define the route:

@app.get("/", tags=["Root"])
async def read_root():
  return { 
    "message": "Welcome to my notes application, use the /docs route to proceed"
   }

To run the application, you have to define an entry point in the main.py file. In the entry point, we’ll be using Uvicorn to run the server, as indicated earlier:

//main.py
import uvicorn

if __name__ == "__main__":
  uvicorn.run("server.api:app", host="0.0.0.0", port=8000, reload=True)

In the main block, we invoke the run method from Uvicorn and take in the following parameters:

  • Location of the FastAPI instance
  • Host address
  • Port
  • Boolean reload value

Run the main.py file:

(venv)$ python3 main.py

The above command should return an output like the one below in our command line:

INFO:     Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
INFO:     Started reloader process [20586] using statreload
INFO:     Started server process [20588]
INFO:     Waiting for application startup.
INFO:     Application startup complete.

The application can be viewed on the browser on http://0.0.0.0:8000. We will be using Postman/Insomnia to test our application endpoints.

Feel free to swap any of these out for FastAPI’s interactive docs on http://0.0.0.0:8000/docs.

Next, send a GET request to http://0.0.0.0:8000 on Postman (or Insomnia):

GET Request

Defining the model schema

Let’s define the model schema for our application. This will represent how data is stored in our application. In the app folder, create a new file, model.py:

from typing import Optional
from pydantic import BaseModel

class NoteSchema(BaseModel):
  title: Optional[str]
  content: Optional[str]

  class Config:
    schema_extra = {
        "example": {
            "title": "LogRocket.",
            "content": "Logrocket is the most flexible publishing company for technical authors. From editors to payment, the process is too flexible and that's what makes it great."
        }
    }

In the code block above, we defined a Pydantic schema called NoteSchema, which represents how the notes data will be stored in our application’s temporary database. The subclass config holds an example request body, which will guide users when trying to send requests from the interactive docs.

Let’s define the routes for the CRUD operations in the routes.py file in the next section.

Defining our routes

With the schema in place, let’s create an in-app database to store and retrieve our notes and import the notes schema.

routes.py

Start by importing FastAPI’s APIRouter class and NoteSchema:

from fastapi import APIRouter, Body
from fastapi.encoders import jsonable_encoder
from server.model import NoteSchema

router = APIRouter()

Just below the router variable, create a temporary database, notes:

notes = {
    "1": {
        "title": "My first note",
        "content": "This is the first note in my notes application"
    },
    "2": {
        "title": "Uniform circular motion.",
        "content": "Consider a body moving round a circle of radius r, wit uniform speed v as shown below. The speed everywhere is the same as v but direction changes as it moves round the circle."
    }
}

Next, define the routes for GET requests:

@router.get("/")
async def get_notes() -> dict:
    return {
        "data": notes
    }

@router.get("/{id}")
async def get_note(id: str) -> dict:
    if int(id) > len(notes):
        return {
            "error": "Invalid note ID"
        }

    for note in notes.keys():
        if note == id:
            return {
                "data": notes[note]
            }

In the code block above, we defined two routes:

  1. A route to /note to return all the available notes
  2. A route to /note/{id} to return a note with an ID matching the one passed

Before proceeding to test the routes, include the notes router in the global route handler in api.py, like this:

from server.routes import router as NoteRouter

...

app.include_router(NoteRouter, prefix="/note")

The FastAPI().include_router() method is used to include routes declared in other files in the global route handler. This method comes in handy in applications where you split routes into separate files and directories.

Testing our routes

With the notes route in place, let’s test the routes:

  1. GET /note:
    GET Note
  2. GET /note/{id}: In our temporary database, we added two notes with IDs 1 and 2. Passing an ID that isn’t in the notes database will return an error response. We’ll try both valid and invalid IDs, in that order:
    GET Note ID

Now for an ID that’s not in the database:

Example of ID Not in Database

Next, define the POST route to add a new note:

@router.post("/note")
async def add_note(note: NoteSchema = Body(...)) -> dict:
    note.id = str(len(notes) + 1)
    notes[note.id] = note.dict()

    return {
        "message": "Note added successfully"
    }

In the add_note function, we set the note to be of type NoteSchema, our model, and made it a required argument using Body(…). The ellipsis in the Body() statement indicates that this request body must be filled according to the schema specification.

To test the POST route, you need to change the request type from GET to POST in Postman/Insomnia and the URL address to http://0.0.0.0:8000/note. Next, set the request body to JSON and pass in the JSON code below:

{
    "title": "Deploying FastAPI applications to Vercel",
    "content": "In this article, you will be learning how to build and in turn deploy a FastAPI application to Vercel."
}

Now, send the request:

Send Request

The note has been added successfully. Run a GET request on the /note endpoint to verify the addition:

Run GET Request

Next, define the update and delete routes:

@router.put("/{id}")
def update_note(id: str, note: NoteSchema):
    stored_note = notes[id]
    if stored_note:
        stored_note_model = NoteSchema(**stored_note)
        update_data = note.dict(exclude_unset=True)
        updated_note = stored_note_model.copy(update=update_data)
        notes[id] = jsonable_encoder(updated_note)
        return {
            "message": "Note updated successfully"
        }
    return {
        "error": "No such with ID passed exists."
    }


@router.delete("/{id}")
def delete_note(id: str) -> dict:
    if int(id) > len(notes):
        return {
            "error": "Invalid note ID"
        }

    for note in notes.keys():
        if note == id:
            del notes[note]
            return {
                "message": "Note deleted"
            }

    return {
        "error": "Note with {} doesn't exist".format(id)
    }

In the update route, we are performing a partial update. We only update a note if the note exists; otherwise, we return an error message. We also apply the same logic to the delete route. We first check whether the note exists before deleting; otherwise, we return an error message. Let’s go on to test the routes.

Here’s the update route:

Update Route

Now let’s delete the second note to test the delete route:

Delete Route Test

With the routes in place and tested, we can proceed with deploying to Vercel.

Deploying our FastAPI app to Vercel

In this section, we’ll be deploying to Vercel. If you don’t have the Vercel command line tool installed, you can get it by running the following command:

yarn global add vercel

Next, log on:

vercel login

To deploy to Vercel, a vercel.json configuration file is needed. Create a vercel.json file in the parent directory and add the following JSON code:

{
  "builds": [
    {"src": "/server/api.py", "use": "@now/python"}
  ],
  "routes": [
    {"src": "/(.*)", "dest": "server/api.py"}
  ]
}

In the code block above, the builds key holds an array containing another object. In this object, we indicated the path to the application’s entry point. We also stated the package to be used when building our app in the routes object. We direct all routing to the server/api.py file.

Before we proceed to deployment, let’s create a requirements.txt file containing our application dependencies:

//requirements.txt
fastapi
uvicorn

With the configuration and requirements file in place, let’s initialize Vercel. Run this command in the parent directory:

vercel .

Follow the prompt in the console, and we should see a similar screen:

Vercel Prompt in Console

We have successfully deployed our application to Vercel in just four simple steps. We can preview the deployed application by clicking the links in the console or from our Vercel dashboard:

Viewing Deployed Application

The application deployed in this article can be viewed here.

Conclusion

In this article, we have learned how to build and deploy a FastAPI application. You can read more on FastAPI from the official docs and you can find the code used in this article on GitHub.

: Full visibility into your web apps

LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.

.
Abdulazeez Abdulazeez Adeshina Software enthusiast, writer, food lover, and hacker.

Leave a Reply