December 26, 2024

In Part 1 we saw how to get started with FASTAPI and writing simple GET endpoints. In this post, we will see how we can configure dynamic routes to accept variables in the get request. Please note I am not referring to passing query parameters here. We will see how to use query parameters in another post but for now, let’s see how we can build on our previous Python API using FASTAPI.

The requirement is to have an endpoint to fetch detail of any hostname or any model that we want, provided it exists in the database.

@app.get("/routers/hostname/{hostname}")
def get_router(hostname:str): return { "data" : router for router in routers if router['hostname']== hostname } @app.get("/routers/model/{model}")
def get_router(model:str): return { "data" : router for router in routers if router['model']== model }

Now if you access the web browser at “http://127.0.0.1:8000/routers/hostname/rtr-indi-ce01”, you will be presented with the details of only the specific host that you asked for rather than all routers. Similarly, you can fetch the details for specific router models too by “http://127.0.0.1:8000/routers/model/ISR2821”

Data Validation

If you have noticed, in the function definition, we are accepting a variable along with the datatype that we expect. For example

def get_router(model:str)

This means model is expected to be a string but what happens if you pass an integer rather than a string ?

We don’t get any error message on the screen even if we have passed an integer 10 and that is because by default the entire URL is treated as a string which we can verify from the logs by using below technique

@app.get("/routers/model/{model}")
def get_router(model:str): print(type(model)) return { "data" : router for router in routers if router['model']== model } <class 'str'>
INFO: 127.0.0.1:55439 - "GET /routers/model/10 HTTP/1.1" 200 OK

But what if we modify the method to accept the model variable as an integer

@app.get("/routers/model/{model}")
def get_router(model:int): print(type(model)) return { "data" : router for router in routers if router['model']== model } <class 'int'>
INFO: 127.0.0.1:55457 - "GET /routers/model/10 HTTP/1.1" 200 OK

For the same API request “http://127.0.0.1:8000/routers/model/10”, we still don’t get any error message because of the automatic type conversion where 10 now is interpreted as an integer but what happens now when you modify the request to contain an actual string value while still expecting int as input?

Example:- “http://127.0.0.1:8000/routers/model/ISR2821”

The above error message clearly indicates that “type” field has a type error where an integer is expected and the value is not a valid integer value and even on the CLI, we see this message

INFO: 127.0.0.1:55503 - "GET /routers/model/ISR2821 HTTP/1.1" 422 Unprocessable Entity
INFO: 127.0.0.1:55553 - "GET /routers/model/ISR2821 HTTP/1.1" 422 Unprocessable Entity

Another important concept is Order of Routes is extremely important.

The API endpoints/routes/path operations are evaluated in the order they are defined in the python script file. For demonstration, if I modify the paths like below

@app.get("/")
def get_routers(): return {"data": device_list} @app.get("/routers")
def get_routers(): return {"data": routers} @app.get("/routers/hostname/{hostname}")
def get_router(hostname:str): return { "data" : router for router in routers if router['hostname']== hostname } @app.get("/routers/hostname/rtr-indi-ce01")
def get_router(): return { "data" : router for router in routers if router['hostname']== 'rtr-indi-ce01'}, {'value': "Route sequence demo"}

@app.get(“/routers/hostname/{hostname}”) is evaluated and then @app.get(“/routers/hostname/rtr-indi-ce01”) is evaluated. So when a GET request is seen like

http://127.0.0.1:8000/routers/hostname/rtr-indi-ce01 or http://127.0.0.1:8000/routers/hostname/rtr-usam-ce01 or http://127.0.0.1:8000/routers/hostname/rtr-cana-ce01 or anything for that matter it will always match the first in sequence and the static hostname route for rtr-indi-ce01 will never get matched and hence never returned. We will always see the output from @app.get(“/routers/hostname/{hostname}”). However, now if we swap the order like so

@app.get("/routers/hostname/rtr-indi-ce01")
def get_router(): return { "data" : router for router in routers if router['hostname']== 'rtr-indi-ce01'}, {'value': "Route sequence demo"} @app.get("/routers/hostname/{hostname}")
def get_router(hostname:str): return { "data" : router for router in routers if router['hostname']== hostname }

You will see that requests matching “/routers/hostname/rtr-indi-ce01” will return the appropriate output which in this case as a value attribute along with data attribute too.

while other hostnames still give the same old data back without any value field.

In the next post we will talk about the “QUERY PARAMETERS”

The post Python API Using FASTAPI – For Network Engineers – Dynamic Routes – Part II appeared first on Network Automation.

Source