Skip to content

Enable direct response, stac-fastapi 5.2.0, deprecation warnings #359

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Apr 22, 2025
Merged
63 changes: 41 additions & 22 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,24 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

### Changed

### Fixed

## [v4.0.0a2] - 2025-04-20

### Added
- Added support for high-performance direct response mode for both Elasticsearch and Opensearch backends, controlled by the `ENABLE_DIRECT_RESPONSE` environment variable. When enabled (`ENABLE_DIRECT_RESPONSE=true`), endpoints return Starlette Response objects directly, bypassing FastAPI's jsonable_encoder and Pydantic serialization for significantly improved performance on large search responses. **Note:** In this mode, all FastAPI dependencies (including authentication, custom status codes, and validation) are disabled for all routes. Default is `false` for safety. A warning is logged at startup if enabled. See [issue #347](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/issues/347) and [PR #359](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/359).
- Added robust tests for the `ENABLE_DIRECT_RESPONSE` environment variable, covering both Elasticsearch and OpenSearch backends. Tests gracefully handle missing backends by attempting to import both configs and skipping if neither is available. [#359](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/359)

### Changed
- Updated test suite to use `httpx.ASGITransport(app=...)` for FastAPI app testing (removes deprecation warning). [#359](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/359)
- Updated stac-fastapi parent libraries to 5.2.0. [#359](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/359)
- Migrated Elasticsearch index template creation from legacy `put_template` to composable `put_index_template` API in `database_logic.py`. This resolves deprecation warnings and ensures compatibility with Elasticsearch 7.x and 8.x. [#359](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/359)
- Updated all Pydantic models to use `ConfigDict` instead of class-based `Config` for Pydantic v2 compatibility. This resolves deprecation warnings and prepares for Pydantic v3. [#359](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/359)
- Migrated all Pydantic `@root_validator` validators to `@model_validator` for Pydantic v2 compatibility. [#359](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/359)
- Migrated startup event handling from deprecated `@app.on_event("startup")` to FastAPI's recommended lifespan context manager. This removes deprecation warnings and ensures compatibility with future FastAPI versions. [#359](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/359)
- Refactored all boolean environment variable parsing in both Elasticsearch and OpenSearch backends to use the shared `get_bool_env` utility. This ensures robust and consistent handling of environment variables such as `ES_USE_SSL`, `ES_HTTP_COMPRESS`, and `ES_VERIFY_CERTS` across both backends. [#359](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/359)


### Fixed

## [v4.0.0a1] - 2925-04-17
Expand Down Expand Up @@ -343,25 +361,26 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Use genexp in execute_search and get_all_collections to return results.
- Added db_to_stac serializer to item_collection method in core.py.

[Unreleased]: https://github.com/stac-utils/stac-fastapi-elasticsearch/tree/v4.0.0a1...main
[v4.0.0a1]: https://github.com/stac-utils/stac-fastapi-elasticsearch/tree/v4.0.0a0...v4.0.0a1
[v4.0.0a0]: https://github.com/stac-utils/stac-fastapi-elasticsearch/tree/v3.2.5...v4.0.0a0
[v3.2.5]: https://github.com/stac-utils/stac-fastapi-elasticsearch/tree/v3.2.4...v3.2.5
[v3.2.4]: https://github.com/stac-utils/stac-fastapi-elasticsearch/tree/v3.2.3...v3.2.4
[v3.2.3]: https://github.com/stac-utils/stac-fastapi-elasticsearch/tree/v3.2.2...v3.2.3
[v3.2.2]: https://github.com/stac-utils/stac-fastapi-elasticsearch/tree/v3.2.1...v3.2.2
[v3.2.1]: https://github.com/stac-utils/stac-fastapi-elasticsearch/tree/v3.2.0...v3.2.1
[v3.2.0]: https://github.com/stac-utils/stac-fastapi-elasticsearch/tree/v3.1.0...v3.2.0
[v3.1.0]: https://github.com/stac-utils/stac-fastapi-elasticsearch/tree/v3.0.0...v3.1.0
[v3.0.0]: https://github.com/stac-utils/stac-fastapi-elasticsearch/tree/v2.4.1...v3.0.0
[v2.4.1]: https://github.com/stac-utils/stac-fastapi-elasticsearch/tree/v2.4.0...v2.4.1
[v2.4.0]: https://github.com/stac-utils/stac-fastapi-elasticsearch/tree/v2.3.0...v2.4.0
[v2.3.0]: https://github.com/stac-utils/stac-fastapi-elasticsearch/tree/v2.2.0...v2.3.0
[v2.2.0]: https://github.com/stac-utils/stac-fastapi-elasticsearch/tree/v2.1.0...v2.2.0
[v2.1.0]: https://github.com/stac-utils/stac-fastapi-elasticsearch/tree/v2.0.0...v2.1.0
[v2.0.0]: https://github.com/stac-utils/stac-fastapi-elasticsearch/tree/v1.1.0...v2.0.0
[v1.1.0]: https://github.com/stac-utils/stac-fastapi-elasticsearch/tree/v1.0.0...v1.1.0
[v1.0.0]: https://github.com/stac-utils/stac-fastapi-elasticsearch/tree/v0.3.0...v1.0.0
[v0.3.0]: https://github.com/stac-utils/stac-fastapi-elasticsearch/tree/v0.2.0...v0.3.0
[v0.2.0]: https://github.com/stac-utils/stac-fastapi-elasticsearch/tree/v0.1.0...v0.2.0
[v0.1.0]: https://github.com/stac-utils/stac-fastapi-elasticsearch/tree/v0.1.0
[Unreleased]: https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/compare/v4.0.0a2...main
[v4.0.0a2]: https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/compare/v4.0.0a1...v4.0.0a2
[v4.0.0a1]: https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/compare/v4.0.0a0...v4.0.0a1
[v4.0.0a0]: https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/compare/v3.2.5...v4.0.0a0
[v3.2.5]: https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/compare/v3.2.4...v3.2.5
[v3.2.4]: https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/compare/v3.2.3...v3.2.4
[v3.2.3]: https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/compare/v3.2.2...v3.2.3
[v3.2.2]: https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/compare/v3.2.1...v3.2.2
[v3.2.1]: https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/compare/v3.2.0...v3.2.1
[v3.2.0]: https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/compare/v3.1.0...v3.2.0
[v3.1.0]: https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/compare/v3.0.0...v3.1.0
[v3.0.0]: https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/compare/v2.4.1...v3.0.0
[v2.4.1]: https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/compare/v2.4.0...v2.4.1
[v2.4.0]: https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/compare/v2.3.0...v2.4.0
[v2.3.0]: https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/compare/v2.2.0...v2.3.0
[v2.2.0]: https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/compare/v2.1.0...v2.2.0
[v2.1.0]: https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/compare/v2.0.0...v2.1.0
[v2.0.0]: https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/compare/v1.1.0...v2.0.0
[v1.1.0]: https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/compare/v1.0.0...v1.1.0
[v1.0.0]: https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/compare/v0.3.0...v1.0.0
[v0.3.0]: https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/compare/v0.2.0...v0.3.0
[v0.2.0]: https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/compare/v0.1.0...v0.2.0
[v0.1.0]: https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/compare/v0.1.0
24 changes: 18 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,18 @@
- There is [Postman](https://documenter.getpostman.com/view/12888943/2s8ZDSdRHA) documentation here for examples on how to run some of the API routes locally - after starting the elasticsearch backend via the compose.yml file.
- The `/examples` folder shows an example of running stac-fastapi-elasticsearch from PyPI in docker without needing any code from the repository. There is also a Postman collection here that you can load into Postman for testing the API routes.

- For changes, see the [Changelog](CHANGELOG.md)
- We are always welcoming contributions. For the development notes: [Contributing](CONTRIBUTING.md)

### Performance Note

The `enable_direct_response` option is provided by the stac-fastapi core library (introduced in stac-fastapi 5.2.0) and is available in this project starting from v4.0.0.

**You can now control this setting via the `ENABLE_DIRECT_RESPONSE` environment variable.**

When enabled (`ENABLE_DIRECT_RESPONSE=true`), endpoints return Starlette Response objects directly, bypassing FastAPI's default serialization for improved performance. **However, all FastAPI dependencies (including authentication, custom status codes, and validation) are disabled for all routes.**

This mode is best suited for public or read-only APIs where authentication and custom logic are not required. Default is `false` for safety.

See: [issue #347](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/issues/347)


### To install from PyPI:
Expand Down Expand Up @@ -74,8 +84,9 @@ If you wish to use a different version, put the following in a
file named `.env` in the same directory you run Docker Compose from:

```shell
ELASTICSEARCH_VERSION=7.17.1
OPENSEARCH_VERSION=2.11.0
ELASTICSEARCH_VERSION=8.11.0
OPENSEARCH_VERSION=2.11.1
ENABLE_DIRECT_RESPONSE=false
```
The most recent Elasticsearch 7.x versions should also work. See the [opensearch-py docs](https://github.com/opensearch-project/opensearch-py/blob/main/COMPATIBILITY.md) for compatibility information.

Expand All @@ -100,8 +111,9 @@ You can customize additional settings in your `.env` file:
| `RELOAD` | Enable auto-reload for development. | `true` | Optional |
| `STAC_FASTAPI_RATE_LIMIT` | API rate limit per client. | `200/minute` | Optional |
| `BACKEND` | Tests-related variable | `elasticsearch` or `opensearch` based on the backend | Optional |
| `ELASTICSEARCH_VERSION` | ElasticSearch version | `7.17.1` | Optional |
| `OPENSEARCH_VERSION` | OpenSearch version | `2.11.0` | Optional |
| `ELASTICSEARCH_VERSION` | Version of Elasticsearch to use. | `8.11.0` | Optional |
| `ENABLE_DIRECT_RESPONSE` | Enable direct response for maximum performance (disables all FastAPI dependencies, including authentication, custom status codes, and validation) | `false` | Optional |
| `OPENSEARCH_VERSION` | OpenSearch version | `2.11.1` | Optional |

> [!NOTE]
> The variables `ES_HOST`, `ES_PORT`, `ES_USE_SSL`, and `ES_VERIFY_CERTS` apply to both Elasticsearch and OpenSearch backends, so there is no need to rename the key names to `OS_` even if you're using OpenSearch.
Expand Down
4 changes: 2 additions & 2 deletions compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ services:
environment:
- STAC_FASTAPI_TITLE=stac-fastapi-elasticsearch
- STAC_FASTAPI_DESCRIPTION=A STAC FastAPI with an Elasticsearch backend
- STAC_FASTAPI_VERSION=4.0.0a1
- STAC_FASTAPI_VERSION=4.0.0a2
- APP_HOST=0.0.0.0
- APP_PORT=8080
- RELOAD=true
Expand Down Expand Up @@ -41,7 +41,7 @@ services:
environment:
- STAC_FASTAPI_TITLE=stac-fastapi-opensearch
- STAC_FASTAPI_DESCRIPTION=A STAC FastAPI with an Opensearch backend
- STAC_FASTAPI_VERSION=4.0.0a1
- STAC_FASTAPI_VERSION=4.0.0a2
- APP_HOST=0.0.0.0
- APP_PORT=8082
- RELOAD=true
Expand Down
4 changes: 2 additions & 2 deletions examples/auth/compose.basic_auth.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ services:
environment:
- STAC_FASTAPI_TITLE=stac-fastapi-elasticsearch
- STAC_FASTAPI_DESCRIPTION=A STAC FastAPI with an Elasticsearch backend
- STAC_FASTAPI_VERSION=4.0.0a1
- STAC_FASTAPI_VERSION=4.0.0a2
- APP_HOST=0.0.0.0
- APP_PORT=8080
- RELOAD=true
Expand Down Expand Up @@ -42,7 +42,7 @@ services:
environment:
- STAC_FASTAPI_TITLE=stac-fastapi-opensearch
- STAC_FASTAPI_DESCRIPTION=A STAC FastAPI with an Opensearch backend
- STAC_FASTAPI_VERSION=4.0.0a1
- STAC_FASTAPI_VERSION=4.0.0a2
- APP_HOST=0.0.0.0
- APP_PORT=8082
- RELOAD=true
Expand Down
4 changes: 2 additions & 2 deletions examples/auth/compose.oauth2.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ services:
environment:
- STAC_FASTAPI_TITLE=stac-fastapi-elasticsearch
- STAC_FASTAPI_DESCRIPTION=A STAC FastAPI with an Elasticsearch backend
- STAC_FASTAPI_VERSION=4.0.0a1
- STAC_FASTAPI_VERSION=4.0.0a2
- APP_HOST=0.0.0.0
- APP_PORT=8080
- RELOAD=true
Expand Down Expand Up @@ -43,7 +43,7 @@ services:
environment:
- STAC_FASTAPI_TITLE=stac-fastapi-opensearch
- STAC_FASTAPI_DESCRIPTION=A STAC FastAPI with an Opensearch backend
- STAC_FASTAPI_VERSION=4.0.0a1
- STAC_FASTAPI_VERSION=4.0.0a2
- APP_HOST=0.0.0.0
- APP_PORT=8082
- RELOAD=true
Expand Down
4 changes: 2 additions & 2 deletions examples/auth/compose.route_dependencies.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ services:
environment:
- STAC_FASTAPI_TITLE=stac-fastapi-elasticsearch
- STAC_FASTAPI_DESCRIPTION=A STAC FastAPI with an Elasticsearch backend
- STAC_FASTAPI_VERSION=4.0.0a1
- STAC_FASTAPI_VERSION=4.0.0a2
- APP_HOST=0.0.0.0
- APP_PORT=8080
- RELOAD=true
Expand Down Expand Up @@ -42,7 +42,7 @@ services:
environment:
- STAC_FASTAPI_TITLE=stac-fastapi-opensearch
- STAC_FASTAPI_DESCRIPTION=A STAC FastAPI with an Opensearch backend
- STAC_FASTAPI_VERSION=4.0.0a1
- STAC_FASTAPI_VERSION=4.0.0a2
- APP_HOST=0.0.0.0
- APP_PORT=8082
- RELOAD=true
Expand Down
4 changes: 2 additions & 2 deletions examples/rate_limit/compose.rate_limit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ services:
environment:
- STAC_FASTAPI_TITLE=stac-fastapi-elasticsearch
- STAC_FASTAPI_DESCRIPTION=A STAC FastAPI with an Elasticsearch backend
- STAC_FASTAPI_VERSION=4.0.0a1
- STAC_FASTAPI_VERSION=4.0.0a2
- APP_HOST=0.0.0.0
- APP_PORT=8080
- RELOAD=true
Expand Down Expand Up @@ -42,7 +42,7 @@ services:
environment:
- STAC_FASTAPI_TITLE=stac-fastapi-opensearch
- STAC_FASTAPI_DESCRIPTION=A STAC FastAPI with an Opensearch backend
- STAC_FASTAPI_VERSION=4.0.0a1
- STAC_FASTAPI_VERSION=4.0.0a2
- APP_HOST=0.0.0.0
- APP_PORT=8082
- RELOAD=true
Expand Down
6 changes: 3 additions & 3 deletions stac_fastapi/core/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
"attrs>=23.2.0",
"pydantic>=2.4.1,<3.0.0",
"stac_pydantic~=3.1.0",
"stac-fastapi.api==5.1.1",
"stac-fastapi.extensions==5.1.1",
"stac-fastapi.types==5.1.1",
"stac-fastapi.api==5.2.0",
"stac-fastapi.extensions==5.2.0",
"stac-fastapi.types==5.2.0",
"orjson~=3.9.0",
"overrides~=7.4.0",
"geojson-pydantic~=1.0.0",
Expand Down
9 changes: 6 additions & 3 deletions stac_fastapi/core/stac_fastapi/core/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ async def item_collection(
search=search,
limit=limit,
sort=None,
token=token, # type: ignore
token=token,
collection_ids=[collection_id],
)

Expand Down Expand Up @@ -633,7 +633,7 @@ async def post_search(
items, maybe_count, next_token = await self.database.execute_search(
search=search,
limit=limit,
token=search_request.token, # type: ignore
token=search_request.token,
sort=sort,
collection_ids=search_request.collections,
)
Expand Down Expand Up @@ -701,7 +701,10 @@ async def create_item(
database=self.database, settings=self.settings
)
processed_items = [
bulk_client.preprocess_item(item, base_url, BulkTransactionMethod.INSERT) for item in item["features"] # type: ignore
bulk_client.preprocess_item(
item, base_url, BulkTransactionMethod.INSERT
)
for item in item["features"]
]

await self.database.bulk_async(
Expand Down
4 changes: 2 additions & 2 deletions stac_fastapi/core/stac_fastapi/core/extensions/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from types import DynamicClassAttribute
from typing import Any, Callable, Dict, Optional

from pydantic import BaseModel, root_validator
from pydantic import BaseModel, model_validator
from stac_pydantic.utils import AutoValueEnum

from stac_fastapi.extensions.core.query import QueryExtension as QueryExtensionBase
Expand Down Expand Up @@ -63,7 +63,7 @@ class QueryExtensionPostRequest(BaseModel):

query: Optional[Dict[str, Dict[Operator, Any]]] = None

@root_validator(pre=True)
@model_validator(mode="before")
def validate_query_fields(cls, values: Dict) -> Dict:
"""Validate query fields."""
...
Expand Down
29 changes: 29 additions & 0 deletions stac_fastapi/core/stac_fastapi/core/utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,42 @@
This module contains functions for transforming geospatial coordinates,
such as converting bounding boxes to polygon representations.
"""
import logging
import os
from typing import Any, Dict, List, Optional, Set, Union

from stac_fastapi.types.stac import Item

MAX_LIMIT = 10000


def get_bool_env(name: str, default: bool = False) -> bool:
"""
Retrieve a boolean value from an environment variable.

Args:
name (str): The name of the environment variable.
default (bool, optional): The default value to use if the variable is not set or unrecognized. Defaults to False.

Returns:
bool: The boolean value parsed from the environment variable.
"""
value = os.getenv(name, str(default).lower())
true_values = ("true", "1", "yes", "y")
false_values = ("false", "0", "no", "n")
if value.lower() in true_values:
return True
elif value.lower() in false_values:
return False
else:
logger = logging.getLogger(__name__)
logger.warning(
f"Environment variable '{name}' has unrecognized value '{value}'. "
f"Expected one of {true_values + false_values}. Using default: {default}"
)
return default


def bbox2polygon(b0: float, b1: float, b2: float, b3: float) -> List[List[List[float]]]:
"""Transform a bounding box represented by its four coordinates `b0`, `b1`, `b2`, and `b3` into a polygon.

Expand Down
2 changes: 1 addition & 1 deletion stac_fastapi/core/stac_fastapi/core/version.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
"""library version."""
__version__ = "4.0.0a1"
__version__ = "4.0.0a2"
2 changes: 1 addition & 1 deletion stac_fastapi/elasticsearch/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
desc = f.read()

install_requires = [
"stac-fastapi-core==4.0.0a1",
"stac-fastapi-core==4.0.0a2",
"elasticsearch[async]~=8.18.0",
"uvicorn~=0.23.0",
"starlette>=0.35.0,<0.36.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@
api = StacApi(
title=os.getenv("STAC_FASTAPI_TITLE", "stac-fastapi-elasticsearch"),
description=os.getenv("STAC_FASTAPI_DESCRIPTION", "stac-fastapi-elasticsearch"),
api_version=os.getenv("STAC_FASTAPI_VERSION", "2.1"),
api_version=os.getenv("STAC_FASTAPI_VERSION", "4.0.0a2"),
settings=settings,
extensions=extensions,
client=CoreClient(
Expand Down
Loading