Creating an API with python: Part 1: GET Endpoints

In this post, I’ll show you how I set up a simple API using python’s FastAPI, with five GET endpoints. If you read my Creating an API with AWS series, you may recognise the endpoints. My eventual aim is to recreate the API I built in AWS using python.

Prerequisites

These are not strictly prerequisites, but you may find it useful to follow my series on Launching a CentOS7 Virtual Machine on Windows10, which will take you through the steps for setting up a CentOS7 virtual machine, with the end result being a virtual machine configured as an API server running FastAPI:

  1. Launching a CentOS7 Virtual Machine on Windows10
  2. Launching a CentOS7 Virtual Machine on Windows10: Part 2: Networking
  3. Launching a CentOS7 Virtual Machine on Windows10: Part 3: Shared Folder
  4. Launching a CentOS7 Virtual Machine on Windows10: Part 4: API Server

Step 1: Install FastAPI

See Launching a CentOS7 Virtual Machine on Windows10: Part 4: API Server for details on how to install FastAPI and configure a virtual environment to run the FastAPI server. The rest of this post will assume you have been able to get FastAPI running with the root endpoint which returns: {"message": "Hello World"}.

Step 2: Add GET endpoints to main.py

Open main.py and replace the code with the following:

from typing import Optional

from fastapi import FastAPI, Query, Path, Body, HTTPException

app = FastAPI()

LINKS = [{'link_id': '1', 'link': 'https://google.com'}, {'link_id': '2', 'link': 'https://amazon.com'}]
TAGS = [{'tag_id': '1', 'tag': 'search'}, {'tag_id': '2', 'tag': 'shop'}]
TAG_LINKS = [{'link_id': '1', 'tag_id': '1'}, {'link_id': '2', 'tag_id': '2'}]


def get_tag_id(tag: str):
    tag_id = None
    for tag_detail in TAGS:
        if tag_detail['tag'] == tag:
            tag_id = tag_detail['tag_id']
            break
    return tag_id


def fetch_tag(tag_id: str):
    for tag in TAGS:
        if tag['tag_id'] == tag_id:
            return tag
    return None


# Get link by link_id
@app.get("/link/{link_id}")
async def get_link(link_id: str):
    for link in LINKS:
        if link['link_id'] == link_id:
            return link
    raise HTTPException(status_code=404, detail="Link not found")


# Get links by query params
@app.get("/link/")
async def get_links(tag_id: Optional[str] = None, tag: Optional[str] = None):
    link_ids = []
    return_links = []
    if tag is None and tag_id is None:
        return LINKS

    if tag is not None:
        tag_id = get_tag_id(tag)

    if tag_id is not None:
        for tag_link in TAG_LINKS:
            if tag_link['tag_id'] == tag_id:
                link_ids.append(tag_link['link_id'])

    for link in LINKS:
        for link_id in link_ids:
            if link_id == link['link_id']:
                return_links.append(link)
    return return_links


# Get tag by tag_id
@app.get("/tag/{tag_id}")
async def get_tag(tag_id: str):
    tag = fetch_tag(tag_id)
    if tag is not None:
        return tag
    raise HTTPException(status_code=404, detail=f"Tag with tag_id {tag_id} not found")


# Get tags by query params
@app.get("/tag/")
async def get_tags(tag: Optional[str] = None):
    return_tags = []
    if tag is None:
        return TAGS
    else:
        for tag_detail in TAGS:
            if tag_detail['tag'] == tag:
                return_tags.append(tag_detail)
    return return_tags


# Get taglinks by query params
@app.get("/taglink/")
async def get_taglinks(link_id: Optional[str] = None, tag_id: Optional[str] = None):
    return_taglinks = []
    if link_id is None and tag_id is None:
        return TAG_LINKS
    elif link_id is not None and tag_id is not None:
        for tag_link in TAG_LINKS:
            if tag_link['link_id'] == link_id and tag_link['tag_id'] == tag_id:
                return_taglinks.append(tag_link)

    elif link_id is not None:
        for tag_link in TAG_LINKS:
            if tag_link['link_id'] == link_id:
                return_taglinks.append(tag_link)
    else:
        # tag_id is not None
        for tag_link in TAG_LINKS:
            if tag_link['tag_id'] == tag_id:
                return_taglinks.append(tag_link)

    return return_taglinks

Step 3: Start FastAPI

Start the uvicorn API server, setting host to 0.0.0.0.0 so it will listen on any available interface:

$ uvicorn --host 0.0.0.0 main:app --reload

Step 4: Test FastAPI

  1. With the API server running, navigate to http://YOUR_IP:8000/docs in your browser, replacing YOUR_IP with either the IP set up in Launching a CentOS7 Virtual Machine on Windows10: Part 2: Networking, the IP of the virtual machine/server running FastAPI, or 127.0.0.1 if running FastAPI on your local machine.
  2. You should see the swagger documentation for the GET endpoints. If you click on them in turn, then click ‘Try it out’, then enter parameter values in the form fields, then click ‘Execute’, you should see the response in the ‘Response Body’ box.
  3. Start a new PuTTY SSH session to your virtual machine/FastAPI server (if not running locally).
  4. Try running:
    curl -X 'GET' 'http://YOUR_IP:8000/link/' -H 'accept: application/json'
    
    You should get the response:
    [
      {
        "link_id": "1",
        "link": "https://google.com"
      },
      {
        "link_id": "2",
        "link": "https://amazon.com"
      }
    ]
    
  5. Try running:
    curl -X 'GET' 'http://YOUR_IP:8000/tag/' -H 'accept: application/json'
    
    You should get the response:
    [
      {
        "tag_id": "1",
        "tag": "search"
      },
      {
        "tag_id": "2",
        "tag": "shop"
      }
    ]
    
  6. Try running:
    curl -X 'GET' 'http://YOUR_IP:8000/taglink/' -H 'accept: application/json'
    
    You should get the response:
    [
      {
        "link_id": "1",
        "tag_id": "1"
      },
      {
        "link_id": "2",
        "tag_id": "2"
      }
    ]
    
  7. Try running:
    curl -X 'GET' 'http://YOUR_IP:8000/link/1' -H 'accept: application/json'
    
    You should get the response:
    {
      "link_id": "1",
      "link": "https://google.com"
    }
    
  8. Try running:
    curl -X 'GET' 'http://YOUR_IP:8000/tag/1' -H 'accept: application/json'
    
    You should get the response:
    {
      "tag_id": "1",
      "tag": "search"
    }
    

Conclusion

You should now have a FastAPI API running with five GET endpoints, that can be called via a swagger (docs) page or via curl.

If you want to find out how to add a MariaDB database backend to the API, see my follow-on post, Creating an API with python: Part 2: MariaDB Database.

Thanks for reading!