Python for Network Engineers

Build a REST API With FAST API

by: George El., December 2020, Reading time: 3 minutes

In this post I will show you how to build quickly a REST API using the package FAST API

The endpoints for each will be as shown

endpoints

First create a virtual environment and install fast api and uvicorn

python3 -m venv env
source env/bin/activate
pip install fastapi, uvicorn

Instead of putting all the endpoints in one file we will use a modular approach and use one file for the routers and one for the switches. The file for routers routers_view.py

from fastapi import APIRouter, Path
from pydantic import BaseModel
from typing import List
import json

router_view = APIRouter()

with open('app/views/routers.json') as f:
    router_list = json.load(f)['routers']


class Router(BaseModel):
    rtr_id:int
    model:str
    category:str
    IOS:str
    IOS_Image:str



@router_view.get("/")
async def get_routers():
    return {"routers": router_list }

@router_view.get("/{rtr_id}")
async def get_router(rtr_id:int):
    return { "router" : router for router in router_list if router['rtr_id']==rtr_id }

@router_view.post("/")
async def create_router(rtr:Router):
    router_list.append(rtr)
    return { "router":rtr }

@router_view.put("/")
async def update_router(rtr:Router):
    for router in router_list:
        if router['rtr_id']==rtr.rtr_id:
            router.update(rtr)
    return { "router":rtr }

@router_view.delete("/{rtr_id}")
async def delete_router(rtr_id: int):
    global router_list
    router_list = [router for router in router_list if router['rtr_id']!=rtr_id]
    return { f"router{rtr_id}": " deleted" }

For backend we will use a json file which we will load to memory. Please note this is only for demonstration purposes. Having a global variable is not thread safe and it does not make sense if you spawn multiple processes. In reality your backend will be a database.

The json file is

{
    "routers": [
        {
            "rtr_id": 100,
            "model": "isr4321",
            "category": "branch",
            "IOS":"IOS-XE",
            "IOS_Image":"15.5.3"
       },
       {
            "rtr_id": 101,
            "model": "ASR-1001-x",
            "category": "WAN_Agg",
            "IOS":"IOS-XE",
            "IOS_Image":"Amsterdam-17.3.2"
        },
        {
            "rtr_id": 102,
            "model": "isr4321",
            "category": "branch",
            "IOS":"IOS-XE",
            "IOS_Image":"15.5.1"
       },
       {
            "rtr_id": 103,
            "model": "ASR-1001-x",
            "category": "WAN_Agg",
            "IOS":"IOS-XE",
            "IOS_Image":"Everest-16.6.2"
        }
    ]
}

For switches switches_view.py

from fastapi import APIRouter, Path
from pydantic import BaseModel
from typing import List
import json

switch_view = APIRouter()
with open('app/views/switches.json') as f:
    switch_list = json.load(f)['switches']



class Switch(BaseModel):
    rtr_id:int
    model:str
    category:str
    IOS:str
    IOS_Image:str

@switch_view.get("/")
async def get_switches():
    return {"switches": switch_list }

@switch_view.get("/{sw_id}")
async def get_switch(sw_id:int):
    return { "switch" : switch for switch in switch_list if switch['sw_id']==sw_id }

@switch_view.post("/")
async def create_switch(sw:Switch):
    switch_list.append(sw)
    return { "switch":sw }

@switch_view.put("/")
async def update_switch(sw:Switch):
    for switch in switch_list:
        if switch['sw_id']==sw.sw_id:
            switch.update(sw)
    return { "switch":sw }

@switch_view.delete("/{sw_id}")
async def delete_switch(sw_id: int):
    global switch_list
    switch_list = [switch for switch in switch_list if switch['sw_id']!=sw_id]
    return { f"switch {sw_id}": " deleted" }

and switches.json

{
    "switches": [
        {
            "sw_id": 100,
            "model": "3750",
            "category": "branch",
            "IOS":"IOS",
            "IOS_Image":"15.5.3"
       },
       {
            "sw_id": 101,
            "model": "3650",
            "category": "branch",
            "IOS":"IOS",
            "IOS_Image":"15.5.2"
        },
        {
            "sw_id": 102,
            "model": "nexus3000",
            "category": "branch",
            "IOS":"IOS-XE",
            "IOS_Image":"15.5.1"
       },
       {
            "sw_id": 103,
            "model": "nexus3000",
            "category": "branch",
            "IOS":"IOS-XE",
            "IOS_Image":"Everest-16.6.2"
        }
    ]
}

our main.py file

from fastapi import FastAPI

from .views.routers_view import router_view
from .views.switches_view import switch_view

app = FastAPI()


app.include_router(router_view,prefix="/routers",tags=["routers"])
app.include_router(switch_view,prefix="/switches",tags=["switches"])

so we have a folder app where are main.py is and a folder views where our routers_view.py and switches_view.py reside. to run it, you write uvicorn app.main:app

$uvicorn app.main:app
INFO:     Started server process [17800]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO:     127.0.0.1:54514 - "GET /docs HTTP/1.1" 200 OK
INFO:     127.0.0.1:54514 - "GET /openapi.json HTTP/1.1" 200 OK

if you then get to http://127.0.0.1:8000/docs you will get the first picture. this provides documentation and allows you to run all rest call from there.

if you go to http://127.0.0.1:8000/routers/100 you will get


{
  "router": {
    "rtr_id": 100,
    "model": "isr4321",
    "category": "branch",
    "IOS": "IOS-XE",
    "IOS_Image": "15.5.3"
  }
}

In a later example I will show you how to connect to an sql database and read the values from there

comments powered by Disqus