At some point in your career, you’re likely to build a REST API for either yourself or your organization. This article is designed to strip the basics of building a REST API in Python down to the absolute minimum without going too much into detail.

Here, we’ll be building a basic REST API in Python using FastAPI. Before showing you how to build it, there’s 2 things you need to know:

  1. The full code can be found in this GitHub Gist that also includes code to call the REST API outlined here.

  2. There’s a lot of details I’m leaving out (such as FastAPI vs Django vs Flask, error handling, etc) because this can get quite lengthy.

What is a REST API?

First, we need to understand what an API is. API stands for Application Programming Interface, and it’s a way to interact with someone else’s/your own server to perform certain operations, such as retrieving and updating data.

This seems like a very vague description, but let’s suppose I have a database full of data you need for your application. Rather than you go and getting the data yourself, I can expose an API for you to hit my database to retrieve the data.

REST (Representational State Transfer) API is an architectural pattern to follow protocols to retrieve data. It defines how data is transferred between systems over the internet.

When you build a REST API, you are responsible for how users access the data, the logic to be able to retrieve the data, and the structure as to how it’s returned to the user.

Building your first REST API

We also need to understand the 5 basic request types that you’ll likely be using for your application:

HTTP Request

Description

GET

Retrieves the data

POST

Creates data

PUT

Updates data

PATCH

Partially updates data

DELETE

Deletes the data

We’ll be using FastAPI to build this application. For each type of HTTP request, we can add the decorator @app.<REQUEST TYPE>(“<endpoint>”). An endpoint is a specific URL where we can make requests; this will make more sense later down the line.

To start our application, we’ll need to install 4 libraries: pandas, fastapi, requests and uvicorn. We’ll also set up the database as items.csv:

id,name,description
1,Apple,A tasty red fruit
2,Banana,A long yellow fruit

We’ll then set up the application:

from fastapi import FastAPI, HTTPException
import pandas as pd
from pathlib import Path

CSV_FILE = Path("items.csv")
app = FastAPI()

def load_df():
    return pd.read_csv(CSV_FILE)

def save_df(df):
    df.to_csv(CSV_FILE, index=False)

GET Request

GET requests allow you to retrieve an item from the API. We can implement it using the decorator @app.get():

@app.get("/items")
def get_items():
    df = load_df()
    return df.to_dict(orient="records")

» To see how we can set up a get request to get items by a specific ID, visit the GitHub Gist.

POST Request

POST requests creates a new resource. In our instance, it will create a new row in our CSV file. Implement this using the @app.post() method:

@app.post("/items")
def create_item(item: dict):
    df = load_df()

    if df[df["id"] == item.get("id")].shape[0] > 0:
        raise HTTPException(400, "ID already exists")

    df = pd.concat(
        [df, pd.DataFrame([item])], 
        ignore_index=True
    )
    save_df(df)

    return item

PUT Request

PUT requests updates a resource. Implement this using the @app.put() method, passing in the ID of the row we want to update using brackets in the endpoint field (The ID is how we tell our server which item to update):

@app.put("/items/{item_id}")
def replace_item(item_id: int, item: dict):
    df = load_df()

    if item_id not in df["id"].values:
        raise HTTPException(404, "Item not found")

    # Get row index to update
    row_index = df.index[df["id"] == item_id][0]

    # Replace the entire row explicitly
    df.loc[row_index, "id"] = item["id"]
    df.loc[row_index, "name"] = item["name"]
    df.loc[row_index, "description"] = item["description"]

    save_df(df)
    return item

PATCH Request

PATCH requests is very similar to a PUT request, with a major caveat - PUT requests replaces everything while PATCH replaces parts of the resource. This is extremely important to understand because if you have a missing field, a PUT request will erase whatever is there and put in the missing field.

To implement this, use @app.patch(), passing in the ID of the resource (exactly like PUT):

@app.patch("/items/{item_id}")
def update_item(item_id: int, updates: dict):
    df = load_df()

    if item_id not in df["id"].values:
        raise HTTPException(404, "Item not found")

    row_index = df.index[df["id"] == item_id][0]

    for key, value in updates.items():
        df.at[row_index, key] = value

    save_df(df)

    return df.iloc[row_index].to_dict()

DELETE Request

This deletes a specific resource, implemented with @app.delete(), passing in the ID of your resource:

@app.delete("/items/{item_id}")
def delete_item(item_id: int):
    df = load_df()

    if item_id not in df["id"].values:
        raise HTTPException(404, "Item not found")

    df = df[df["id"] != item_id]
    save_df(df)

    return {"status": "deleted"}

» Reminder, you can visit this GitHub gist to get the full code, including how you’d make a request to your server when you run it with uvicorn app:app --debug

Happy coding!

📧 Join the Python Snacks Newsletter! 🐍

Want even more Python-related content that’s useful? Here’s 3 reasons why you should subscribe the Python Snacks newsletter:

  1. Get Ahead in Python with bite-sized Python tips and tricks delivered straight to your inbox, like the one above.

  2. Exclusive Subscriber Perks: Receive a curated selection of up to 6 high-impact Python resources, tips, and exclusive insights with each email.

  3. Get Smarter with Python in under 5 minutes. Your next Python breakthrough could just an email away.

You can unsubscribe at any time.

Interested in starting a newsletter or a blog?

Do you have a wealth of knowledge and insights to share with the world? Starting your own newsletter or blog is an excellent way to establish yourself as an authority in your field, connect with a like-minded community, and open up new opportunities.

If TikTok, Twitter, Facebook, or other social media platforms were to get banned, you’d lose all your followers. This is why you should start a newsletter: you own your audience.

This article may contain affiliate links. Affiliate links come at no cost to you and support the costs of this blog. Should you purchase a product/service from an affiliate link, it will come at no additional cost to you.

Reply

or to participate

Keep Reading

No posts found