Skip to content

Getting started

Install gazebo and stand up a minimal OGC-style API.

Install

pip install gazebo            # core: pydantic only
pip install 'gazebo[fastapi]' # + the GazeboApp / FastAPI glue

Requires Python 3.12+.

Your first app

The app below is complete and runnable. It shows the three ideas you'll use in every gazebo service: an endpoint on a GazeboRouter returning a LinkedCollection of deferred links; one app-scoped dependency injected by type; and a create_app() factory so tests can pass overrides. (A real service would also inject a database and a request-derived user — see Gazebo Gardens.)

from dataclasses import dataclass

from gazebo.collection import LinkedCollection
from gazebo.ext.fastapi import GazeboApp, GazeboRouter, Overrides, Providers
from gazebo.link import Link


@dataclass
class Settings:
    greeting: str = 'hello'

    @classmethod
    def __provide__(cls) -> Settings:
        return cls()


class Things(LinkedCollection[dict], items_alias='things'):
    pass


router = GazeboRouter()


@router.get('/things', response_model=Things)
async def list_things(settings: Settings, limit: int = 10) -> Things:
    items = [{'id': i, 'greeting': settings.greeting} for i in range(limit)]
    return Things(items=items, links=[Link.self_link(), Link.root_link()])


def create_app(overrides: Overrides | None = None) -> GazeboApp:
    providers = Providers().app(Settings)
    app = GazeboApp(providers, overrides=overrides)
    app.include_router(router)

    @app.get('/', name='landing')
    async def landing() -> dict:
        return {'service': 'things'}

    return app


app = create_app()

What just happened

Four gazebo features are already at work:

  • Links resolved at serialize time. Link.self_link() / root_link() are built with no request in hand; the glue fills in real URLs when the response serializes — see Links.
  • A collection envelope. Things adds numberReturned and a configurable items alias for free — see Collections.
  • By-type injection. settings is resolved from the registry by its type, no Depends — see Dependency injection.
  • Proxy-correct URLs. Put this behind a load balancer and the links follow X-Forwarded-* — see Proxy & context.

Testing it

Tests drive the app through TestClient and substitute config with Overrides passed into create_app — by parameter, never by mutating a global, so tests stay isolated:

from fastapi.testclient import TestClient


def test_things() -> None:
    overrides = Overrides().set(Settings, Settings(greeting='hi'))
    with TestClient(create_app(overrides)) as client:
        body = client.get('/things?limit=2').json()
        assert body['numberReturned'] == 2
        assert body['things'][0]['greeting'] == 'hi'

Next