Skip to content

Query Parameters

You can declare "query" parameters by defining function parameters with Query().

from typing import Annotated

from nexify import Nexify, Query

app = Nexify()

fake_items_db = [{"name": "Foo"}, {"name": "Bar"}, {"name": "Baz"}]


@app.get("/items")
def read_items(skip: Annotated[int, Query()] = 0, limit: Annotated[int, Query()] = 10):
    return fake_items_db[skip : skip + limit]

A query parameter is a key-value pair appended to a URL after a question mark (?).

Multiple query parameters can be included by separating them with an ampersand (&), forming a single string.

For example, in the following URL:

예를 들어, 아래의 URL에서

/items?skip=0&limit=10

The query parameters are:

  • skip: with a value of 0
  • limit: with a value of 10

As they are part of the URL, they are "naturally" strings.

But when you declare them with Python types (in the example above, as int), they are converted to that type and validated against it.

All the same process that applied for path parameters also applies for query parameters:

  • Editor support: error checks, autocompletion, etc.
  • Data "parsing"
  • Data validation
  • API annotation and automatic documentation

Default Values

Query parameters are not fixed parts of the path, meaning they can be optional and have default values.

Info

Unlike query parameters, path parameters cannot be optional or have default values.

In this case, skip has a default value of 0, and limit has a default value of 10.

So, going to /items would be teh same as going to /items?skip=0&limit=10.

But if you go to /items?skip=20

The query parameter values in your function will be:

  • skip=20: because you set it in the URL
  • limit=10: because that was the default value

Declaring Default Values in Different Ways

You can also set default values by passing the default parameter inside Query() within Annotated.

from typing import Annotated

from nexify import Nexify, Query

app = Nexify()

fake_items_db = [{"name": "Foo"}, {"name": "Bar"}, {"name": "Baz"}]


@app.get("/items")
def read_items(skip: Annotated[int, Query(default=0)], limit: Annotated[int, Query(default=10)]):
    return fake_items_db[skip : skip + limit]

In this case, skip defaults to 0, and limit defaults to 10.

Using this way allows you to bypass Python's restriction that parameters without default values must precede those with default values, which can be useful in some situations.


Alternatively, you can set default values using the default_factory parameter inside Query() within Annotated.

from datetime import datetime
from typing import Annotated

from nexify import Nexify, Query

app = Nexify()


def default_date():
    return datetime.now().isoformat()


@app.get("/logs")
def get_logs(
    start_date: Annotated[str, Query(default_factory=default_date)],
    end_date: Annotated[str, Query(default_factory=default_date)],
):
    return {"start_date": start_date, "end_date": end_date}

The default_factory is extremely useful in various situations:

  • When a dynamic value is needed for each request: for example, datetime.now() returns a different value each time it is called.
  • When using mutable objects as default values: instead of manually checking for None, you can set default_factory=list or default_factory=dict to ensure a new list or dictionary is created for each request.
  • When providing computed default values: if a default value needs to be derived from logic, wrapping it in a function and passing it to default_factory keeps the code clean.

Data Validation

from typing import Annotated

from nexify import Nexify, Query

app = Nexify()

fake_items_db = [{"name": "Foo"}, {"name": "Bar"}, {"name": "Baz"}]


@app.get("/items")
def read_items(skip: Annotated[int, Query()] = 0, limit: Annotated[int, Query()] = 10):
    return fake_items_db[skip : skip + limit]

If you go to /items?skip=foo, you will see a 422 response like this:

{
  "detail": [
    {
      "type": "int_parsing",
      "loc": [
        "query",
        "skip"
      ],
      "msg": "Input should be a valid integer, unable to parse string as an integer",
      "input": "foo"
    }
  ]
}

Just like in the path parameters tutorial, since "foo" cannot be converted to an int, an internal Pydantic-based validation error occurs, and Nexify returns a 422 response with a detailed error message.

Documentation

And go to /docs, you will see the following Swagger UI:

Swagger UI

확인

Since Nexify automatically generates API documentation based on Python type hints, you can see that the path parameter is correctly documented as an int type. Their default values are also clearly displayed as 0 and 10.

Required Query Parameters

Query parameters with default values are always "optional". To make a query parameter "required", simply do not provide a default value.

from typing import Annotated

from nexify import Nexify, Query

app = Nexify()

fake_items_db = [{"name": "Foo"}, {"name": "Bar"}, {"name": "Baz"}]


@app.get("/items")
def read_items(skip: Annotated[int, Query()], limit: Annotated[int, Query()]):
    return fake_items_db[skip : skip + limit]

if you go to /items without query parameters, you will receive a 422 response like this:

{
  "detail": [
    {
      "loc": ["query", "skip"],
      "msg": "Field required",
      "type": "missing",
      "input": null
    },
    {
      "loc": ["query", "limit"],
      "msg": "Field required",
      "type": "missing",
      "input": null
    }
  ]
}

Since no default values are provided, skip and limit are "required" query parameters, and their absence triggers a validation error.

Additionally, the documentation clearly indicates that these parameters are "required".

Swagger UI