Deploy a Python API on Vercel using Postgres

Tags: technology, tutorial, postgres, python
Published: 2022-02-08 23:55:58

APIs have changed how software is built in recent years, allowing for more reusable code that can interact with any software development tool. Modern APIs have gained defined standards (usually HTTP and REST) that are developer-friendly, easily available, and widely understood, making it easier for developers to build maintainable code with security checks in place, as well as comprehensive documentation.

[FastAPI](https://fastapi.tiangolo.com/) is a high-performing Python web framework for creating APIs with standard Python-type hints, allowing you to easily create speedy, intuitive, and robust web applications with fewer bugs. In addition, it has built-in support for API documentation, powered by Swagger.

In this tutorial, we'll learn how to build and deploy a Postgres FastAPI application on Vercel by creating a simple task manager application. To follow along, ensure you clone the [Github](https://github.com/icode247/PostgresWithFastAPI) repository for this project. Let's get started!.

## Prerequisites

This tutorial is a hands-on demonstration. To follow along, ensure you have the following installed:

- [Arctype](https://arctype.com/)
- [Postgres Database](https://www.postgresql.org/download/)
- [Postman](https://www.postman.com/downloads/)
- [Python](https://www.python.org/downloads/)
- [Heroku CLI](https://devcenter.heroku.com/articles/heroku-cli)

## What is Vercel?

[Vercel](https://vercel.com/) is a cloud hosting platform, widely regarded as the best place to deploy any frontend application. Vercel provides the flexibility of zero-configuration deployment to its global edge network, enabling dynamic application scalability without breaking a sweat.

Vercel combines the most remarkable development experience with obsessive attention to end-user efficiency, as well as a slew of exciting features like:

- **Fast refresh**: a dependable live-editing experience for your UI components.
- **Versatile data fetching**: Connect your pages to any data source, headless CMS, or API to ensure that they work in everyone's development environment.
- **Localhost perfection**: All your cloud primitives, from caching to serverless functions, run flawlessly on localhost.

## Setting up our project

Before we dive too deep, let's create our project structure and install the dependencies needed for our application. We'll start by creating the project folder. Open your terminal and run the following commands:

```
mkdir PostgresWithFastAPI && cd PostgresWithFastAPI
touch {main,database,model,schema,session}.py
```

After running the command above and creating a virtual environment for our project (which we'll cover in the next section), our project structure will look like this:

![Screen Shot 2022-02-08 at 5.33.43 PM.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1644363227586/tnhBOQZ7P.png)

We'll be working with each of these files throughout this tutorial.

## Creating the virtual environment

It is always good to create a virtual environment for the Python projects you build. A virtual environment will contain your dependencies for the project and isolate them, keeping your project neatly contained. We'll create a virtual environment for this project by using `virtualenv`: 

```
pip install virtualenv
```

Now, create and activate your virtual environment by running the commands below:

```
python3 -m venv env
source env/bin/activate
```

We have successfully created a virtual environment for the project. We'll also eeed to install **Fastapi**, **Uvicorn**, **Sqlalchemy**, and **psycopg2-binary** with the command below:

```
pip install fastapi uvicorn sqlalchemy psycopg2-binary
```

Now run the command below to save our dependence in a *requirements.txt* file.

```
pip freeze > requirements.txt
```

Excellent. Now, let's go ahead and create our FastAPI server.

## Creating a FastAPI server

With our project set up, we can now create our FastAPI server. First, open the `main.py` file in the project's root directory and add the following code to it:

```
from fastapi import FastAPI
app = FastAPI()

@app.get("/")
def read_root():
    return {"message": "Server is up and running!"}
```

Next, navigate to the project root directory on your terminal, and test out the server by running the command below:

```
uvicorn main:app --reload
```
The  **--reload** flag we added to the command tells FastAPI to watch for updates on our codebase, and reload the server if it finds one. Now, make a **Get request** to the server to ensure everything is working with [Postman](https://www.postman.com/downloads/).

![](https://lh6.googleusercontent.com/KEKAzywzRJFZAKE_YKW0O-6hykJDuJq_zXvJCY62xh1-5fytnytXsp4IOHHmKIP2TysDo6lc7yUYc8q0KhphpcBLgQUn2s2Kmn8XH4yhOacFQvOQ-fLHjyrTY90y27KKi6QdEc5H)

Our server has successfully been created and running. Next, we'll need a database to save our user's records. Let's go ahead and set up one.

## Setup the Postgres database

We can now set up our Postgres database to store our user's records with our server setup. We'll use [SQLAlchemy ORM](https://www.sqlalchemy.org/) (Object Relational Mapper) to connect our database with our application. To begin, we'll need to create a database with the following steps. First, switch to the system's Postgres user account.

```
sudo su - postgres
```

Then, create a new user account. You can then proceed by following the screenshot below.

```
createuser --interactive
```

![](https://lh6.googleusercontent.com/6-cDkkNBB4CQwXoh2cicvUF3EvK9pKTnlOauBYiZOLyqaZTkdRVHISXLN3TKepwESw8V5PNoQ5qdruYRcrWsKOBFh3GuG7Yxo9yMYbUGMJ3_j8mXMpXHE5NJKz8F3Ew0aQAGHA1N)

Next, create a new database. You can do that with the following command:

```
createdb task
```

Now, we’ll connect to the database we just created. Open the `database.py` file, and add the following code snippet below:

```
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
SQLALCHEMY_DATABASE_URL = "postgresql://postgres:1234@localhost:5432/task"
engine = create_engine(SQLALCHEMY_DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
```

In the above code snippet, a connection is made to our database using the **create_engine** class we import from **SqlAlchemy**. We also created a **Sessionlocal** instance from the **sessionmaker** class.We disabled **autocommit** and **autoflush**, then bound the database engine to the session. Finally, we created a **Base** instance from the **declarative_base** class, which we'll use to create our application's database model, and our database connection.

## Creating our database model

With our Postgres database setup, let's define how the user's data will be stored by creating a model. Open the `model.py` file and add the following code snippet to it.

```
from sqlalchemy.schema import Column
from sqlalchemy.types import String, Integer, Text
from database import Base

class Task(Base):
   __tablename__ = "Tasks"
   id = Column(Integer, primary_key=True, index=True)
   task_name = Column(String(20))
   task_des = Column(Text())
   created_by = Column(String(20))
   date_created = Column(String(15))
```

In the above code snippet, we defined our data models by importing **Column** and passing the data types we expect to store for each of the fields in our database (`Integer` , `String()`, and `Text()`).

We also imported the **Base** instance we created in our **database.py** file which we used to create our base model class. Then we set our table name(**Tasks**) using the **__tablename__** attribute. To create a distinction between the data stored in our database table, we added the **primary_key** and **index** parameters to our **id** field and set them to true.

## Creating our database schema

Let's define a schema for our application. We need to define a [Pydantic](https://pydantic-docs.helpmanual.io/) schema that will read data and return it from the API. Open the `Schema.py` file and add the following code snippet to it:

```
from pydantic import BaseModel
from typing import Optional

class task_schema(BaseModel):
   task_name :str
   task_des :str
   created_by : Optional[str]= None
   date_created : Optional[str]= None

   class Config:
       orm_mode = True
```

In the above code snippet, we defined our model validations, which ensures the data coming from the client-side is the same data type as the field we defined. We will be expecting a string value for our **task_name** and **task_des** fields, and optional string values for the **created_by** and **date_created** fields. The subclass config with **orm_mode** set to **True** will instruct the Pydantic model to read the data as a dictionary and attribute.

## Create our app routes

With our schema created, let's define the routes of our application. To begin, open the `session.py` file and create a **create_get_session()** function to create and close the session our routes with the code snippet below:

```
import model
from database import SessionLocal, engine

model.Base.metadata.create_all(bind=engine)
def create_get_session():
   try:
       db = SessionLocal()
       yield db
   finally:
       db.close()
```

In the above code snippet, we created our table using the field we defined in our model by calling the **model.Base.metadata.create_all()** function and binding it to our database engine.

Then, open the ***main.py*** file and import all our modules with the code snippet below:

```
from fastapi import FastAPI, Depends, HTTPException
from sqlalchemy.orm import Session
from typing import List
from model import Task
from schema import task_schema
from session import get_database_session
…
```

Next, create the **read_tasks** routes with the code snippet below:

```
…
@app.get("/task", response_model=List[task_schema], status_code=200)
async def read_tasks(db: Session = Depends(create_get_session)):
   tasks = db.query(Task).all()
   return tasks
…
```

In the above code snippet, we created a **read_tasks** route which will listen to a **GET request.** We passed in our response model, which returns a list of all the tasks in our database and a status code of 200 (**OK**). In our **read_tasks** function, referenced our model session which will enable us to execute queries in our database.

Next, create a **create_task** route, to add new tasks to our database with the code snippet below:

```
…
@app.post('/task', response_model = task_schema, status_code=201)
async def create_task(task: task_schema, db: Session = Depends(create_get_session)):
   new_task = Task(
        task_name = task.task_name,
        task_des = task.task_des,
        created_by =task.created_by,
        datecreated = task.date_created,
   )
   db.add(new_task)
   db.commit()

   return new_task
…
```

In the code snippet above, we created a **create_task** route, which will listen to POST requests. This time our response model will return the task that was just created with a status code of **201(Created)**. Then we get the data from the request body by passing an **item** and assigning it to our Pydantic model. Also, we created a **new_task** object from our model class and passed in the data from our request body to the fields in our model. Then we add the **new_task** object to our database session, commit them and return the created object.

Next, we create the **get_task** route, which will return a task whose id is specified in the request parameter with the code snippet below:

```
…
@app.get("/task/{id}", response_model = task_schema, status_code=200)
async def get_task(id:int,db: Session = Depends(create_get_session)):
   task = db.query(Task).get(id)
   return task
…
```

In the above code snippet, we created our **get_task** route which will also listen to a **GET** request. But this time, we passed in the **id** of the task as a parameter in our endpoint. Our response model will return an item object with a status code of **200(OK)**. Then we query our database model (**Tasks**) to get the task whose id is specified in the request parameter and return it to the users.

Next, we'll create our **update_task** route, which will listen to a **PATCH** request, we also pass in our response model, which will return the task object that was updated with a status code of **200 (OK)**. Then we query our model for the item whose id is specified in the request parameter, reset the values of the task, save it to the databases, refresh the database, and return the updated record object to the user.

```
…
@app.patch("/task/{id}", response_model = task_schema, status_code=200)
async def update_task(id:int, task:task_schema, db: Session = Depends(create_get_session)):
   db_task = db.query(Task).get(id)
   db_task.task_name = task.task_name
   db_task.task_des =  task.task_des
   db.commit()
   db.refresh(db_task)

   return db_task
…
```

Finally, we'll create the **delete_task** route, which will listen to a DELETE request and delete the task whose **id** is specified in the request parameter and return a status code of **200 (OK)**. We'll query our database for the item and raise an HTTPException error if the item does not exist in the database. Then pass in a status code of **404 (NOT FOUND)**, and return null.

```
@app.delete('/task/{id}', status_code=200)
async def delete_task(id:int, db: Session = Depends(create_get_session)):
   db_task = db.query(Task).get(id)
   if not db_task:
       raise HTTPException(status_code="404",detail="Task id does not exist")
 
   db.delete(db_task)
   db.commit()

   return None
```

With that, we've set up all our routes. Let's get our application tested locally.

## Testing the application

With our application setup, let's go ahead test it out with Postman. We'll start with the **Post** route.

![Screenshot of Postman](https://lh5.googleusercontent.com/cp3MQD5snMTRyQ4tYqNgHWiJFmFBStEhW3vrugXa1px8zSc28ImflNWtj0G9vm-sNJxyPm--VPr9hWQUW7_QHgZJ97aCmp8kKZ92YxMw4n9IznBEVsATWwTpVTDwUc5mTdpxjB5E)

Next, the **GET** route. We'll test it the same way.

![Screenshot of Postman](https://lh3.googleusercontent.com/OSiU3i5ByuuvMexUIeNT4jCGRYcT3LsgP543WBdFtsdj875L7BkZEOog0kZskiRo-nsduyeMfWgT_AxjB7J4Z6xh-s8vWPu7facpDIcTzcpOqKW-8PuPMNqRvbHaG6cnkJ10qKZ8)

Next, the **GET** route by **id**. You should see the results shown below.

![Screenshot of Postman](https://lh5.googleusercontent.com/-RXQN8a2N-Fy76fSN5-C0rugqVuh3z9aVB0BYI-w9KIci-X9qcsJB8S8o-qV1r0_o4ihfc0NRA0Pw6dxeJxLHpoNE3FET_uqx8Zs_A13zhSYaNUcHdvXiDPE0VSbPCOZ6B7kwTAm)

Next is the **PATCH** route. Again, the correct response will look like the screen below.

![](https://lh6.googleusercontent.com/BQzsGtgTiWup9i6dCE4lvEBdGUtLmKYedpBk0j5cMBuMr60a-6iDk6BjhtDKSdcdeKykuYeI7uydHLt9PLdNyC7-7tphHwRPMAjNCtP07AS6kXL3cwRW_SIp-OYd4r8VBrvnb6N5)

Finally, the **DELETE** route.

![Screenshot of Postman](https://lh6.googleusercontent.com/RDldi694aI0ivBkYbtZsUWYmcUViiOpQhbAzQvgXyJ5JS0Rg0FOQBtHQ1a5vZmt_J5d639jPPkCvTvnbfM8Wu9LMGqNa9ZmVISk_PIHNeDIDn6flBOGBlO7bjPxnYJli3I--IDpZ)

Everything is looking good. Now, let's get our application deployed to the cloud on Vercel.

## Deploy to Vercel

With our routes tested, our application is ready to be deployed to the cloud in Vercel. Before you deploy the app, provision a remote database on Heroku for the project [here](https://devcenter.heroku.com/articles/getting-started-with-python#provision-a-database), and update the database connection string in the **database.py** file.

```
SQLALCHEMY_DATABASE_URL = “Remote connection string”
```

Then [sign up](https://vercel.com/signup) for an account on Vercel, and install the Vercel CLI tool with the command below:

```
//Ubuntu
sudo npm install vercel

//Windows
npm install -g vercel
```

Once the installation is completed, login to the Vercel CLI with the command below:

```
Vercel login
```

The above command will prompt you for the auth account you wish to log in with. Make your choice, and hit the **enter** key. You'll be redirected to a new tab in your browser, if you see a success message, then you successfully logged in to your account. That means you can now access your project from the terminal.

Next, you'll need a configuration file to tell Vercel where to find your **main** project file. Create a **vercel.json** file in the parent directory and add the following JSON code snippet:

```
{
 "builds": [{ "src": "main.py", "use": "@vercel/python" }],
 "routes": [{ "src": "/(.*)", "dest": "main.py" }]
}
```

In the above code snippet,  we created an object that indicates the path to our application's **main** file. We also stated the package to be used when building our app in the **routes** object. We direct all routing to the **main.py** file.

Now, we'll initialize Vercel with the command below:

```
Vercel .
```

The above command will prompt you to fill in your project details. Follow the prompt in the screenshot below to do that.

![](https://lh3.googleusercontent.com/2Bn14GXhLynOkBndHh0YaSg8D3wu4nL-fZ1m1XqnIAUGPQZltccg2DhQZ5LIzydKKiYMOMwWFPRDPo06mfd8YW4xzY0dCRDBgYg1mZzVYGMcu7BxxK2-ydIszomXj4dOfpGCUTs1)

At this point, our project has successfully been deployed on Vercel. You can test it out from the [link](https://fastapidemo.vercel.app/docs).

## Visualize our data with arctype

With our application deployed on Vercel, let's visualize the records in our database by connecting our database to Arctype. To get started, ensure you've downloaded and installed [Arctype](arctype.com) on your machine. Launch Arctype and click on Postgres to create a connection. Click on this [link](https://docs.arctype.com/connect/postgresql) if you need help.

![Screenshot of Arctype](https://lh6.googleusercontent.com/pnX_9PXF1f0hDvDjwtRrGWhOJq2QVKoeRQ8uiTQ1XtK4hxlsFenBDRY6XmJqZxh4EQrSimZ5SBdjaHglGn9NaoR1Fzc1gkXAlKYPlcrNGcseBAwytIvnnWgKmiQlTi8zNRZHc-b9)

After choosing **Postgres,** add your database credentials and click the **save** button to connect to the database.

![Screenshot of Arctype](https://lh3.googleusercontent.com/M6O4sPTviJoekYV81s8COpZcn0a-PRKQ3IlnB96X8NGGdjOCpV5hyGXImGYgl5XpUmRiYlrP6Uu-LDvwQ1eaUV0qANpCQF9TL8KM9Tkes_YloG9HXiH1IjL3GfgiqYkrhkEg61rM)

We have connected to our remote database. Click on the **task** table to run some queries on our database on Arctype. You can perform CRUD operations directly to the database from the **Arctype**, resulting in outputs like the one shown below.

![Screenshot of Arctype](https://arctype.com/blog/content/images/2022/02/Screenshot-from-2022-02-04-20-15-40.png)

Arctype is a powerful SQL client with advanced collaboration and data visualization tools. Be sure to play around and experiment to find the best ways to study your application's data. 

## Conclusion

In this tutorial, you've learned how to build and deploy a Postgres FastAPI application on Vercel. We started with a brief overview of Vercel and FastAPI. Then we created a FastAPI server, set up a Postgres database, connected the application to Postgres, performed CRUD operations, and visualized data with Arctype.

Now that you have this knowledge, how do you intend to build your next FastAPI application? Perhaps you can learn more about [FastAPI](https://fastapi.tiangolo.com/) and [Arctype](https://arctype.com/) from their website and use what you've learned as inspiration for your next project. Feel free to reach out on [Twitter](https://twitter.com/EkekentaZion) and share your progress and questions.