Javier Cárdenas
Javier Cárdenas

Reputation: 4035

How to make a query parameter required depending of another query parameter in FastApi?

I want to make a query parameter required depending of another query parameter in FastApi

For example I have four query parameters: command, start_date, end_date and increment

If command is equal to "analyse", then end_date must be required. But if command is equal to "add_working_days" then end_date is not required but increment is required. Is this possible?

This is my code righ now:

import datetime
from fastapi import FastAPI, Query

app = FastAPI()  

@app.get("/api/")
async def read_item(
    start_date: str = Query(..., regex=r"[\d]{4}-[\d]{1,2}-[\d]{1,2}"),
    end_date: str = Query(..., regex=r"[\d]{4}-[\d]{1,2}-[\d]{1,2}"),
    command: str = None,
    increment: int = None,
):
    parsed_start_date = datetime.datetime.strptime(start_date, "%Y-%m-%d")
    parsed_end_date = datetime.datetime.strptime(end_date, "%Y-%m-%d")
    duration = parsed_end_date - parsed_start_date
    return duration.days

Upvotes: 6

Views: 8112

Answers (2)

RaamEE
RaamEE

Reputation: 3497

What you want to do is possible if you write it like you suggest. You can set default None values to parameters to allow some flexibility with and do the validation of rules like:

If command == analyse:
  Check start_date is valid
  Check end_date is also valid
  Check increment is None

If command == add_working_days:
  Check start_date is valid
  Check end_date is None
  Check increment is also valid

But my opinion is that this is not the proper way to do what you want.

In the above case FastAPI cannot generate documentation that express your parameters combinations in GUI (e.g. https://petstore.swagger.io/)


My opinion is that

Such documentation is your product to any consumers of your API. It will not be clear to them which combination of parameters should be passed together. You will need to provide such documentation as free text in the description.

Since Open API specification was created to help developers understand each other as quick as possible, I suggest you stick to the spec and avoid such rules combinations.

FastAPI's syntax which derives from the Open API specification will not be able to describe URLs that route the behavior of your code to a different function, based on the parameters you choose to pass.

Maybe, Maybe, Maybe if this was Java, maybe Swagger like tools could support your code, since multiple signatures can be written for the same function name, but since this is Python I don't see this as a viable behavior.

-- My Suggestion

Follow the answer of Nguyễn Nhân. As he suggested describe the different URLs and parameters combinations as separate router methods. This will save you the need to design complex IFs. This will clarify to your users that they must pass such and such parameters.


Disclaimer

All the above is what I think is the right way. Others may disagree. If I have to consider one case where I would use one URL to do it all, it would be a case where each optional parameter adds to the expected functionality and is not dependent on any other parameters.

for example:

domain/maps?location=XYZ&
add_markers: boolean
add_traffic: boolean
language: [EN,HE]

etc.

where possible links are

domain/maps?location=XYZ&add_markers=true&add_traffic=true&language=EN
domain/maps?location=XYZ&add_traffic=true&language=HE
domain/maps?location=XYZ&add_markers=true

etc., where each passed parameter is independent of the others.

Upvotes: 3

Nguyễn Nhân
Nguyễn Nhân

Reputation: 311

I recommend to separate your api

import datetime
from fastapi import FastAPI, Query

app = FastAPI()  

@app.get("/api/analyse")
async def read_item_for_analyse(
    start_date: str = Query(..., regex=r"[\d]{4}-[\d]{1,2}-[\d]{1,2}"),
    end_date: str = Query(..., regex=r"[\d]{4}-[\d]{1,2}-[\d]{1,2}"),
    increment: int = None,
):
    # do something 

@app.get("/api/add_working_days")
async def read_item_for_add_working_days(
    start_date: str = Query(..., regex=r"[\d]{4}-[\d]{1,2}-[\d]{1,2}"),
    end_date: str = None,
    increment: int = Query(..., title="Increase working days"),
):
    # do something 

It's clear and easy to understand, or continue with your style and add some "if...else" is enough.

Upvotes: 4

Related Questions