Skip to content

Network Automation

My journey with Network & Cloud Automation

Menu
  • Beginner
  • DevOps-NetDevOps
  • Network Automation
    • Docker
    • Python Libraries
      • NAPALM
      • Netmiko
      • Jinja2
      • Scrapli
      • Yang
  • Cloud Automation
    • Terraform
  • Python 🐍 Tips and Tricks
Menu
fastapi_3

Python API Using FASTAPI – For Network Engineers – Path and Query Parameters – Part III

Posted on November 8, 2021October 29, 2021 by Gurpreet Kochar

To make a properly documented Python API Using FastAPI, we can use fastapi’s Path and Query module to pass in more parameters that give more context to that the path and query variables that we use. For example from previous posts if we consider this little excerpt

PATH PARAMETERS

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

And now if we go to the API docs by going to “http://127.0.0.1:8000/docs”, we can see that there is no title or description given to the hostname field. We can add the description here by using Path parameters and to do so we need to import Path() from fastapi.

from fastapi import Path

@app.get("/routers/hostname/{hostname}")
def get_router(hostname: Optional[str] = Path(
    None, description = 'Enter hostname of the device you want to search', example='rtr-indi-ce01')):
    return { "data" : router for router in routers if router['hostname']== hostname }

And now if we reload the API page, we can see the updated description field.

You can see what all parameters this method supports by looking at the function definition and the extract is also shown below.

def Path(  # noqa: N802
    default: Any,
    *,
    alias: Optional[str] = None,
    title: Optional[str] = None,
    description: Optional[str] = None,
    gt: Optional[float] = None,
    ge: Optional[float] = None,
    lt: Optional[float] = None,
    le: Optional[float] = None,
    min_length: Optional[int] = None,
    max_length: Optional[int] = None,
    regex: Optional[str] = None,
    example: Any = Undefined,
    examples: Optional[Dict[str, Any]] = None,
    deprecated: Optional[bool] = None,
    **extra: Any,
)

QUERY PARAMETERS

Instead of defining the route like “@app.get(“/routers/hostname/{hostname}”)” where we are passing the value of the hostname we want to fetch the details for, we can leverage the URL structure to pass in query parameters instead. You probably would have seen a question mark in the URL multiple times like

http://example.com/path?name=Branch&products=[Journeys,Email]

where we are passing name = Branch and products = [‘Journeys’, ‘Email’] thereby allowing us more flexibility with our API code. Defining a query parameter is quite similar to defining Path variables except in this case we don’t need to define the variable in the app.get route.

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

We are still accepting the hostname variable in the function definition but there is no such declaration in the app.get decorator and this syntax by default tells fastapi to consider hostname as a query parameter and not a path parameter.

Integrating the above function into our existing code, we can execute queries like below and get responses.

This flexibility allows us to pass multiple query parameters like below

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

At the moment, both the query parameters are defined as mandatory but we can always make either of them optional if we need to.

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

We can define the model field as optional using python’s typing module and setting the default to None in this case as an example. We are also modifying the code a little bit to stop python from complaining when model = None because there is no entry in the database with None value for the model field. So now, even if you don’t pass the model field in the query parameter string, it will still return the output.

Ofcourse, nothing stops you from combining the Path and Query parameters together in the same definition.

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

hostname is defined as path parameter above and model is optional query parameter. Just like we defined metadata for Path variables using fastapi’s Path() module, we can do the exact same for query parameters using Query() module.

@app.get("/routers/{hostname}/")
def get_router(hostname: str, model: Optional[str] = Query(
    None, description = 'optional model number field')):
    if model:
        return { "data" : router for router in routers if router['hostname']== hostname and router['model']== model }
    else:
        return { "data" : router for router in routers if router['hostname']== hostname}

the model field is still defined as optional with a default value of None inside Query() and is also given a description. Another way you can validate this is by using the SWAGGER UI of your API.

There is a lot more flexibility that comes along with query parameters like passing in a list of values instead of strings of query parameters.

Read more here

Here are some of the query parameters that you can leverage just like for Path parameters

def Query(  # noqa: N802
    default: Any,
    *,
    alias: Optional[str] = None,
    title: Optional[str] = None,
    description: Optional[str] = None,
    gt: Optional[float] = None,
    ge: Optional[float] = None,
    lt: Optional[float] = None,
    le: Optional[float] = None,
    min_length: Optional[int] = None,
    max_length: Optional[int] = None,
    regex: Optional[str] = None,
    example: Any = Undefined,
    examples: Optional[Dict[str, Any]] = None,
    deprecated: Optional[bool] = None,
    **extra: Any,
)

We have been seeing how to use the READ operation from CRUD stack of operations. In the next section of this post we will see how to configure other CRUD operations like Create / Update / Delete.

Know someone who may benefit? Share this:

  • Tweet
  • Click to share on Telegram (Opens in new window) Telegram
  • Click to share on WhatsApp (Opens in new window) WhatsApp
  • Click to email a link to a friend (Opens in new window) Email
  • More
  • Click to print (Opens in new window) Print
  • Click to share on Reddit (Opens in new window) Reddit
  • Share on Tumblr
  • Pocket

Like this:

Like Loading...

Related

Leave a ReplyCancel reply

All Blog Posts
My Resume

Upcoming Posts

Sorry - nothing planned yet!

Recent Posts

  • How to backup configuration to TFTP Server using Ansible – Part II
  • How to backup network devices using Ansible – Part I
  • Netmiko SSH Proxy/JumpServer
  • A short note on SASE
  • Understanding Ansible

Recent Comments

  1. Jack on Multithreading with Python for Network Engineers
  2. LifeCanvas on [Theory] Multithreading vs Multiprocessing vs AsyncIO
  3. Jasper Horng on Netmiko SSH Proxy/JumpServer
  4. asdfasdf on Python API Using FASTAPI – UPDATE – PUT – PATCH – Part V
  5. Gurpreet Kochar on Python Scrapli AsyncIO Usage

Archives

  • September 2022
  • February 2022
  • January 2022
  • December 2021
  • November 2021
  • October 2021
  • September 2021
  • August 2021
  • July 2021
Topic Request / Suggestion
Loading
© 2025 Network Automation | Powered by Minimalist Blog WordPress Theme
 

Loading Comments...
 

    %d