Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
6570957
route extension, create, get catalogs
jonhealy1 Mar 25, 2026
e05c740
>= python 3.12
jonhealy1 Mar 25, 2026
ad1ccb6
Merge branch 'main' into stac-fastapi-catalogs-extension
jonhealy1 Mar 26, 2026
0964ec0
Merge branch 'main' into stac-fastapi-catalogs-extension
jonhealy1 Apr 10, 2026
ee18b97
update to catalogs extension v0.1.3
jonhealy1 Apr 10, 2026
f127857
Merge branch 'stac-fastapi-catalogs-extension' of https://github.com/…
jonhealy1 Apr 10, 2026
19a166a
lint
jonhealy1 Apr 10, 2026
366cf7d
add to dev deps
jonhealy1 Apr 10, 2026
7e43b20
change class name
jonhealy1 Apr 11, 2026
1e3409b
sub-catalog scratch
jonhealy1 Apr 11, 2026
6487974
advertise ports
jonhealy1 Apr 11, 2026
8fc5d83
sub-catalog links, tests
jonhealy1 Apr 11, 2026
7237f29
lint
jonhealy1 Apr 11, 2026
e8b4503
ensure parent_ids list not returned
jonhealy1 Apr 11, 2026
080e3fe
switch to collection_search pgstac
jonhealy1 Apr 11, 2026
59b10e1
transaction routes scratch
jonhealy1 Apr 15, 2026
2cc1488
fix poly-hierarchy
jonhealy1 Apr 15, 2026
a3ac186
clean up tests
jonhealy1 Apr 15, 2026
2a5fed6
more test clean up
jonhealy1 Apr 15, 2026
a17e996
update docstrings
jonhealy1 Apr 16, 2026
d055d82
check, test links
jonhealy1 Apr 16, 2026
050fab2
lint
jonhealy1 Apr 16, 2026
c7fe5af
Merge branch 'main' into stac-fastapi-catalogs-extension
jonhealy1 Apr 22, 2026
4a9e45d
revert changes to compose, makefile
jonhealy1 Apr 22, 2026
7fec995
fix get all catalogs pagination
jonhealy1 Apr 22, 2026
df73ea7
qa fixes, pagination, items
jonhealy1 Apr 23, 2026
f95b8e5
qa, sub-catalogs
jonhealy1 Apr 23, 2026
19b0935
qa /children
jonhealy1 Apr 23, 2026
bfefc02
update tests
jonhealy1 Apr 23, 2026
9f5d2fa
qa catalog collections
jonhealy1 Apr 23, 2026
107a283
unlink sub-catalogs, collections
jonhealy1 Apr 23, 2026
ebc3411
lint, re factor links
jonhealy1 Apr 23, 2026
3e5c72a
lint
jonhealy1 Apr 23, 2026
d564167
lint
jonhealy1 Apr 23, 2026
c4defa8
ai review
jonhealy1 Apr 23, 2026
424014e
update changelog
jonhealy1 Apr 24, 2026
5af37fc
add to mkdocs
jonhealy1 Apr 24, 2026
f3d3e2d
return 409 conflict
jonhealy1 Apr 24, 2026
938e2f3
fix conformance classes
jonhealy1 Apr 24, 2026
4c5aec1
Merge branch 'main' into stac-fastapi-catalogs-extension
jonhealy1 May 2, 2026
8418971
fix parent_ids in collection
jonhealy1 May 2, 2026
07c7122
pop parent_ids from core collections routes
jonhealy1 May 2, 2026
623d068
fix self link logic
jonhealy1 May 2, 2026
5041db2
add items and queryables links
jonhealy1 May 2, 2026
b8479c9
lint
jonhealy1 May 2, 2026
f933623
use settings in app.py
jonhealy1 May 6, 2026
1372576
raise error
jonhealy1 May 6, 2026
71b4d03
ensure extension available in tests
jonhealy1 May 6, 2026
4f4dfef
update to catalogs extension v0.2.0
jonhealy1 May 6, 2026
0727347
add documentation
jonhealy1 May 6, 2026
5bd245a
change to ENABLE_CATALOGS_EXTENSION
jonhealy1 May 6, 2026
dfcb8ed
enable response models in settings
jonhealy1 May 12, 2026
e292662
generate catalog links helper
jonhealy1 May 14, 2026
e8af111
convert to static methods
jonhealy1 May 14, 2026
620c1a2
mypy
jonhealy1 May 14, 2026
e270a8a
Update stac_fastapi/pgstac/app.py
jonhealy1 May 15, 2026
385e52f
ensure /collections behavior
jonhealy1 May 15, 2026
4ee437e
Add sort, improve errors
jonhealy1 May 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ COPY scripts/wait-for-it.sh scripts/wait-for-it.sh
COPY pyproject.toml pyproject.toml
COPY README.md README.md

RUN python -m pip install .[server]
RUN rm -rf stac_fastapi .toml README.md
RUN python -m pip install -e .[server,catalogs]

RUN groupadd -g 1000 user && \
useradd -u 1000 -g user -s /bin/bash -m user
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile.tests
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ USER newuser
WORKDIR /app
COPY . /app

RUN python -m pip install . --user --group dev
RUN python -m pip install .[catalogs] --user --group dev
8 changes: 6 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ run = docker compose run --rm \
-e APP_PORT=${APP_PORT} \
app

runtests = docker compose run --rm tests
runtests = docker compose -f compose-tests.yml run --rm tests

.PHONY: image
image:
Expand All @@ -22,7 +22,7 @@ docker-run: image

.PHONY: docker-run-nginx-proxy
docker-run-nginx-proxy:
docker compose -f docker-compose.yml -f docker-compose.nginx.yml up
docker compose -f compose.yml -f docker-compose.nginx.yml up

.PHONY: docker-shell
docker-shell:
Expand All @@ -32,6 +32,10 @@ docker-shell:
test:
$(runtests) /bin/bash -c 'export && python -m pytest /app/tests/ --log-cli-level $(LOG_LEVEL)'

.PHONY: test-catalogs
test-catalogs:
$(runtests) /bin/bash -c 'export && python -m pytest /app/tests/test_catalogs.py -v --log-cli-level $(LOG_LEVEL)'

.PHONY: run-database
run-database:
docker compose run --rm database
Expand Down
14 changes: 10 additions & 4 deletions docker-compose.yml → compose-tests.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
services:
app:
image: stac-utils/stac-fastapi-pgstac
restart: always
build: .
environment:
- APP_HOST=0.0.0.0
Expand All @@ -20,15 +21,19 @@ services:
- DB_MAX_CONN_SIZE=1
- USE_API_HYDRATE=${USE_API_HYDRATE:-false}
- ENABLE_TRANSACTIONS_EXTENSIONS=TRUE
ports:
- "8082:8082"
- ENABLE_CATALOGS_ROUTE=TRUE
# ports:
# - "8082:8082"
depends_on:
- database
command: bash -c "scripts/wait-for-it.sh database:5432 && python -m stac_fastapi.pgstac.app"
command: bash -c "scripts/wait-for-it.sh database:5432 && uvicorn stac_fastapi.pgstac.app:app --host 0.0.0.0 --port 8082 --reload"
develop:
watch:
- action: rebuild
- action: sync
path: ./stac_fastapi/pgstac
target: /app/stac_fastapi/pgstac
- action: rebuild
path: ./setup.py

tests:
image: stac-utils/stac-fastapi-pgstac-test
Expand All @@ -40,6 +45,7 @@ services:
- DB_MIN_CONN_SIZE=1
- DB_MAX_CONN_SIZE=1
- USE_API_HYDRATE=${USE_API_HYDRATE:-false}
- ENABLE_CATALOGS_ROUTE=TRUE
command: bash -c "python -m pytest -s -vv"

database:
Expand Down
91 changes: 91 additions & 0 deletions compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
services:
app:
image: stac-utils/stac-fastapi-pgstac
restart: always
build: .
environment:
- APP_HOST=0.0.0.0
- APP_PORT=8082
- RELOAD=true
- ENVIRONMENT=local
- PGUSER=username
- PGPASSWORD=password
- PGDATABASE=postgis
- PGHOST=database
- PGPORT=5432
- WEB_CONCURRENCY=10
- VSI_CACHE=TRUE
- GDAL_HTTP_MERGE_CONSECUTIVE_RANGES=YES
- GDAL_DISABLE_READDIR_ON_OPEN=EMPTY_DIR
- DB_MIN_CONN_SIZE=1
- DB_MAX_CONN_SIZE=1
- USE_API_HYDRATE=${USE_API_HYDRATE:-false}
- ENABLE_TRANSACTIONS_EXTENSIONS=TRUE
- ENABLE_CATALOGS_ROUTE=TRUE
# ports:
# - "8082:8082"
depends_on:
- database
command: bash -c "scripts/wait-for-it.sh database:5432 && uvicorn stac_fastapi.pgstac.app:app --host 0.0.0.0 --port 8082 --reload"
develop:
watch:
- action: sync
path: ./stac_fastapi/pgstac
target: /app/stac_fastapi/pgstac
- action: rebuild
path: ./setup.py

database:
image: ghcr.io/stac-utils/pgstac:v0.9.8
environment:
- POSTGRES_USER=username
- POSTGRES_PASSWORD=password
- POSTGRES_DB=postgis
- PGUSER=username
- PGPASSWORD=password
- PGDATABASE=postgis
ports:
- "5439:5432"
command: postgres -N 500

# Load joplin demo dataset into the PGStac Application
loadjoplin:
image: stac-utils/stac-fastapi-pgstac
environment:
- ENVIRONMENT=development
volumes:
- ./testdata:/tmp/testdata
- ./scripts:/tmp/scripts
command: >
/bin/sh -c "
scripts/wait-for-it.sh -t 60 app:8082 &&
python -m pip install pip -U &&
python -m pip install requests &&
python /tmp/scripts/ingest_joplin.py http://app:8082
"
depends_on:
- database
- app

nginx:
image: nginx
ports:
- ${STAC_FASTAPI_NGINX_PORT:-8080}:80
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- app-nginx
command: [ "nginx-debug", "-g", "daemon off;" ]

app-nginx:
extends:
service: app
command: >
bash -c "
scripts/wait-for-it.sh database:5432 &&
uvicorn stac_fastapi.pgstac.app:app --host 0.0.0.0 --port 8082 --proxy-headers --forwarded-allow-ips=* --root-path=/api/v1/pgstac
"

networks:
default:
name: stac-fastapi-network
5 changes: 4 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "stac-fastapi-pgstac"
description = "An implementation of STAC API based on the FastAPI framework and using the pgstac backend."
readme = "README.md"
requires-python = ">=3.11"
requires-python = ">=3.12"
license = "MIT"
authors = [
{ name = "David Bitner", email = "david@developmentseed.org" },
Expand Down Expand Up @@ -55,6 +55,9 @@ validation = [
server = [
"uvicorn[standard]==0.38.0"
]
catalogs = [
"stac-fastapi-catalogs-extension>=0.1.2",
]

[dependency-groups]
dev = [
Expand Down
52 changes: 46 additions & 6 deletions stac_fastapi/pgstac/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
If the variable is not set, enables all extensions.
"""

import logging
import os
from contextlib import asynccontextmanager
from typing import cast
Expand Down Expand Up @@ -45,13 +46,35 @@
from stac_fastapi.pgstac.config import Settings
from stac_fastapi.pgstac.core import CoreCrudClient, health_check
from stac_fastapi.pgstac.db import close_db_connection, connect_to_db
from stac_fastapi.pgstac.extensions import FreeTextExtension, QueryExtension
from stac_fastapi.pgstac.extensions import (
DatabaseLogic,
FreeTextExtension,
QueryExtension,
)
from stac_fastapi.pgstac.extensions.catalogs.catalogs_client import CatalogsClient
from stac_fastapi.pgstac.extensions.filter import FiltersClient
from stac_fastapi.pgstac.transactions import BulkTransactionsClient, TransactionsClient
from stac_fastapi.pgstac.types.search import PgstacSearch

logger = logging.getLogger(__name__)

# Optional catalogs extension (optional dependency)
try:
from stac_fastapi_catalogs_extension import CatalogsExtension
except ImportError:
CatalogsExtension = None

settings = Settings()


def _is_env_flag_enabled(name: str) -> bool:
"""Return True if the given env var is enabled.

Accepts common truthy values ("yes", "true", "1") case-insensitively.
"""
return os.environ.get(name, "").lower() in ("yes", "true", "1")


# search extensions
search_extensions_map: dict[str, ApiExtension] = {
"query": QueryExtension(),
Expand Down Expand Up @@ -98,11 +121,7 @@

application_extensions: list[ApiExtension] = []

with_transactions = os.environ.get("ENABLE_TRANSACTIONS_EXTENSIONS", "").lower() in [
"yes",
"true",
"1",
]
with_transactions = _is_env_flag_enabled("ENABLE_TRANSACTIONS_EXTENSIONS")
Comment thread
jonhealy1 marked this conversation as resolved.
Outdated
if with_transactions:
application_extensions.append(
TransactionExtension(
Expand Down Expand Up @@ -158,6 +177,27 @@
collections_get_request_model = collection_search_extension.GET
application_extensions.append(collection_search_extension)

# Optional catalogs route
ENABLE_CATALOGS_ROUTE = _is_env_flag_enabled("ENABLE_CATALOGS_ROUTE")
Comment thread
jonhealy1 marked this conversation as resolved.
Outdated
logger.info("ENABLE_CATALOGS_ROUTE is set to %s", ENABLE_CATALOGS_ROUTE)
Comment thread
jonhealy1 marked this conversation as resolved.
Outdated

if ENABLE_CATALOGS_ROUTE:
if CatalogsExtension is None:
logger.warning(
"ENABLE_CATALOGS_ROUTE is set to true, but the catalogs extension is not installed. "
"Please install it with: pip install stac-fastapi-core[catalogs].",
)
Comment thread
jonhealy1 marked this conversation as resolved.
else:
try:
catalogs_extension = CatalogsExtension(
client=CatalogsClient(database=DatabaseLogic()),
enable_transactions=with_transactions,
)
application_extensions.append(catalogs_extension)
print("CatalogsExtension enabled successfully.")
except Exception as e: # pragma: no cover - defensive
logger.warning("Failed to initialize CatalogsExtension: %s", e)
Comment thread
jonhealy1 marked this conversation as resolved.
Outdated


@asynccontextmanager
async def lifespan(app: FastAPI):
Expand Down
10 changes: 9 additions & 1 deletion stac_fastapi/pgstac/extensions/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
"""pgstac extension customisations."""

from .catalogs.catalogs_client import CatalogsClient
from .catalogs.catalogs_database_logic import DatabaseLogic
from .filter import FiltersClient
from .free_text import FreeTextExtension
from .query import QueryExtension

__all__ = ["QueryExtension", "FiltersClient", "FreeTextExtension"]
__all__ = [
"QueryExtension",
"FiltersClient",
"FreeTextExtension",
"CatalogsClient",
"DatabaseLogic",
]
Loading
Loading