Skip to content

stac-fastapi v3.0 Migration Guide

This document aims to help you update your application from stac-fastapi 2.5 to 3.0.0.

CHANGELOG

Removed

  • Removed the Context extension
  • Removed stac_fastapi.api.openapi.config_openapi method and stac_fastapi.api.openapi.VndOaiResponse class
  • Removed response_class argument in stac_fastapi.api.routes.create_async_endpoint method
  • Removed filter_fields property in stac_fastapi.extensions.core.fields.request.PostFieldsExtension class
  • Removed pagination_extension attribute in stac_fastapi.api.app.StacApi
  • Removed default_includes from stac_fastapi.types.config.ApiSettings (#706)
  • Removed use of pagination_extension in register_get_item_collection function (User now need to construct the request model and pass it using items_get_request_model attribute)
  • Removed use of FieldsExtension in stac_fastapi.api.app.StacApi. NOTE: If users use FieldsExtension, they HAVE TO handle skipping the model validation step by returning a JSONResponse from the post_search and get_search client methods
  • Removed add_middleware method in StacApi object and let starlette handle the middleware stack creation (#721)
  • Removed pystac dependecy, as it was just used for a datetime-to-string function (#690)
  • Removed internal Search and Operator Types in favor of stac_pydantic Types (#625)

Changed

  • Update to pydantic 2.0 (#625)
  • Update stac-pydantic requirement to ~3.1 (#697)
  • Switch from fastapi to fastapi-slim to avoid installing unwanted dependencies (#687)
  • Update FastAPI requirement to >=0.111.0
  • Moved AsyncBaseFiltersClient and BaseFiltersClient classes in stac_fastapi.extensions.core.filter.client submodule
  • Add more openapi metadata in input models (#734)
  • Use same Limit (capped to 10_000) for /items and GET - /search input models (#738)
  • Moved GETPagination, POSTPagination, GETTokenPagination and POSTTokenPagination to stac_fastapi.extensions.core.pagination.request submodule (#717)
  • Added option for default route dependencies * can be used for path or method to match all allowed route. (#705)
  • Moved AsyncBaseFiltersClient and BaseFiltersClient classes in stac_fastapi.extensions.core.filter.client submodule (#704)
  • Replace Enum with Literal for FilterLang. (#686)
  • Fix response model validation (#625)
  • Use status code 201 for Item/Collection creation (#625)
  • Add response_class attribute in FilterExtension class
  • Add version pinning (~=3.0) for stac-fastapi submodules

Fixed

  • Updated default filter language in filter extension's POST search request model to match the extension's documentation (#711)
  • Fix missing default (None) for optional query attribute in QueryExtensionPostRequest model (#701)
  • Make str_to_interval not return a tuple for single-value input (fixing datetime argument as passed to get_search). (#692)

Added

  • Add enhanced middleware configuration to the StacApi class, enabling specific middleware options and dynamic addition post-application initialization. (#442)
  • Add response pydantic models to OpenAPI, even if model validation is turned off (#625)
  • Add attributes to stac_fastapi.api.app.StacApi to enable customization of request model for:
  • /collections: collections_get_request_model, default to EmptyRequest
  • /collections/{collection_id}: collection_get_request_model, default to CollectionUri
  • /collections/{collection_id}/items: items_get_request_model, default to ItemCollectionUri
  • /collections/{collection_id}/items/{item_id}: item_get_request_model, default to ItemUri
  • Add Aggregation extension (#684)
  • Add Free-text extension (#655)
  • Add Collection-Search extension (#736, #739)

Dependencies

  • pydantic~=2.0
  • fastapi>=0.111
  • stac-pydantic~=3.1

Most of the stac-fastapi's dependencies have been upgraded. Moving from pydantic v1 to v2 is mostly the one update bringing most breaking changes (see docs.pydantic.dev/latest/migration/).

In addition to pydantic v2 update, stac-pydantic has been updated to better match the STAC and STAC-API specifications (see github.com/stac-utils/stac-pydantic/blob/main/CHANGELOG.md#310-2024-05-21)

Deprecation

  • the ContextExtension have been removed (see stac-utils/stac-pydantic!138) and was replaced by optional NumberMatched and NumberReturned attributes, defined by the OGC features specification.

  • stac_fastapi.api.config_openapi method was removed (see stac-utils/stac-fastapi!523)

  • passing response_class in stac_fastapi.api.routes.create_async_endpoint is now deprecated. The response class now has to be set when registering the endpoint to the application (see stac-utils/stac-fastapi#461)

  • PostFieldsExtension.filter_fields property has been removed.

Middlewares configuration

The StacApi.middlewares attribute has been updated to accept a list of starlette.middleware.Middleware. This enables dynamic configuration of middlewares (see stac-utils/stac-fastapi!442).

# before
class myMiddleware(mainMiddleware):
    option1 = option1
    option2 = option2

stac = StacApi(
    middlewares=[
        myMiddleware,
    ]
)

# now
stac = StacApi(
    middlewares=[
        Middleware(myMiddleware, option1, option2),
    ]
)

Request Models

In stac-fastapi v2.0, users could already customize both GET/POST search request models. For v3.0, we've added more attributes to enable other endpoints customization:

  • collections_get_request_model: GET request model for the /collections endpoint (default to EmptyRequest)
  • collection_get_request_model: GET request model for the /collections/{collection_id} endpoint (default to stac_fastapi.api.models.CollectionUri)
  • items_get_request_model: GET request model for the /collections/{collection_id}/items endpoint (default to stac_fastapi.api.models.ItemCollectionUri)
  • item_get_request_model: GET request model for the /collections/{collection_id}/items/{item_id} endpoint (default to stac_fastapi.api.models.ItemUri)
# before
getSearchModel = create_request_model(
    model_name="SearchGetRequest",
    base_model=BaseSearchGetRequest
    extensions=[...],
    request_type="GET"
)
stac = StacApi(
    search_get_request_model=getSearchModel,
    search_post_request_model=...,
)

# now
@attr.s
class CollectionsRequest(APIRequest):
    user: Annotated[str, Query(...)] = attr.ib()

stac = StacApi(
    search_get_request_model=getSearchModel,
    search_post_request_model=postSearchModel,
        collections_get_request_model=CollectionsRequest,
        collection_get_request_model=...,
        items_get_request_model=...,
        item_get_request_model=...,
)

APIRequest - GET Request Model

Most of the GET endpoints are configured with stac_fastapi.types.search.APIRequest base class.

e.g the BaseSearchGetRequest, default for the GET - /search endpoint:

@attr.s
class BaseSearchGetRequest(APIRequest):
    """Base arguments for GET Request."""

    collections: Optional[List[str]] = attr.ib(default=None, converter=_collection_converter)
    ids: Optional[List[str]] = attr.ib(default=None, converter=_ids_converter)
    bbox: Optional[BBox] = attr.ib(default=None, converter=_bbox_converter)
    intersects: Annotated[Optional[str], Query()] = attr.ib(default=None)
    datetime: Optional[DateTimeType] = attr.ib(
        default=None, converter=_datetime_converter
    )
    limit: Annotated[Optional[int], Query()] = attr.ib(default=10)

We use python attrs to construct those classes. Type Hint for each attribute is important and should be defined using Annotated[{type}, fastapi.Query()] form.

@attr.s
class SomeRequest(APIRequest):
    user_number: Annotated[Optional[int], Query(alias="user-number")] = attr.ib(default=None)

Note: when an attribute has a converter (e.g _ids_converter), the Type Hint should be defined directly in the converter:

def _ids_converter(
    val: Annotated[
        Optional[str],
        Query(
            description="Array of Item ids to return.",
        ),
    ] = None,
) -> Optional[List[str]]:
    return str2list(val)

@attr.s
class BaseSearchGetRequest(APIRequest):
    """Base arguments for GET Request."""

    ids: Optional[List[str]] = attr.ib(default=None, converter=_ids_converter)

Filter extension

default_includes attribute has been removed from the ApiSettings object. If you need defaults includes you can overwrite the FieldExtension models (see stac-utils/stac-fastapi!706).

# before
stac = StacApi(
    extensions=[
        FieldsExtension()
    ]
)

# now
class PostFieldsExtension(requests.PostFieldsExtension):
    include: Optional[Set[str]] = Field(
        default_factory=lambda: {
            "id",
            "type",
            "stac_version",
            "geometry",
            "bbox",
            "links",
            "assets",
            "properties.datetime",
            "collection",
        }
    )
    exclude: Optional[Set[str]] = set()


class FieldsExtensionPostRequest(BaseModel):
    """Additional fields and schema for the POST request."""

    fields: Optional[PostFieldsExtension] = Field(PostFieldsExtension())


class FieldsExtension(FieldsExtensionBase):
    """Override the POST model"""

    POST = FieldsExtensionPostRequest


from stac_fastapi.api.app import StacApi

stac = StacApi(
    extensions=[
        FieldsExtension()
    ]
)

Pagination extension

In stac-fastapi v3.0, we removed the pagination_extension attribute in stac_fastapi.api.app.StacApi. This attribute was used within the register_get_item_collection to update the request model for the /collections/{collection_id}/items endpoint.

It's now up to the user to create the request model and use the items_get_request_model= attribute in the StacApi object.

# before
stac=StacApi(
    pagination_extension=TokenPaginationExtension,
    extension=[TokenPaginationExtension]
)

# now
items_get_request_model = create_request_model(
    "ItemCollectionURI",
    base_model=ItemCollectionUri,
    mixins=[TokenPaginationExtension().GET],
)

stac=StacApi(
    extension=[TokenPaginationExtension],
    items_get_request_model=items_get_request_model,
)

Fields extension and model validation

When using the Fields extension, the /search endpoint should be able to return `invalid STAC Items. This creates an issue when model validation is enabled at the application level.

Previously when adding the FieldsExtension to the extensions list and if setting output model validation, we were turning off the validation for both GET/POST /search endpoints. This was by-passing validation even when users were not using the fields options in requests.

In stac-fastapi v3.0, implementers will have to by-pass the validation step at Client level by returning JSONResponse from the post_search and get_search client methods.

# before
class BadCoreClient(BaseCoreClient):
    def post_search(
        self, search_request: BaseSearchPostRequest, **kwargs
    ) -> stac.ItemCollection:
        return {"not": "a proper stac item"}

    def get_search(
        self,
        collections: Optional[List[str]] = None,
        ids: Optional[List[str]] = None,
        bbox: Optional[List[NumType]] = None,
        intersects: Optional[str] = None,
        datetime: Optional[Union[str, datetime]] = None,
        limit: Optional[int] = 10,
        **kwargs,
    ) -> stac.ItemCollection:
        return {"not": "a proper stac item"}

# now
class BadCoreClient(BaseCoreClient):
    def post_search(
        self, search_request: BaseSearchPostRequest, **kwargs
    ) -> stac.ItemCollection:
        resp = {"not": "a proper stac item"}

        # if `fields` extension is enabled, then we return a JSONResponse
        # to avoid Item validation
        if getattr(search_request, "fields", None):
            return JSONResponse(content=resp)

        return resp

    def get_search(
        self,
        collections: Optional[List[str]] = None,
        ids: Optional[List[str]] = None,
        bbox: Optional[List[NumType]] = None,
        intersects: Optional[str] = None,
        datetime: Optional[Union[str, datetime]] = None,
        limit: Optional[int] = 10,
        **kwargs,
    ) -> stac.ItemCollection:
        resp = {"not": "a proper stac item"}

        # if `fields` extension is enabled, then we return a JSONResponse
        # to avoid Item validation
        if "fields" in kwargs:
            return JSONResponse(content=resp)

        return resp