API for accessing dog breed images and information. Live at api.mgrzmil.dev.
- FastAPI + PostgreSQL with SQLAlchemy, Alembic migrations and PgBouncer-aware connection pooling
- Gated deploy pipeline: deploy → post-deploy smoke tests → npm publish; each stage runs only if the previous one passed
- Verified rollouts:
/healthcheckreturns the deployed commit (X-Git-Sha); smoke tests wait until prod serves the pushed commit before testing - Reusable smoke tests extracted into a versioned GitHub Action, g-marcin/smoke-test-action, shared across services
- Typed client package: OpenAPI spec → TypeScript types published as
@mgrzmil-org/api-typesvia npm trusted publishing (OIDC, no tokens) with provenance; skipped when types are unchanged - Config as code: prod env vars and secrets live in a GitHub
productionenvironment and are rendered onto the VPS on each deploy - Observability: OpenTelemetry tracing (FastAPI + SQLAlchemy), Prometheus
/metrics, and a telemetry relay for the frontend
Every push to main runs deploy.yml:
flowchart LR
A[push to main] --> B[deploy<br/>render .env, git reset,<br/>alembic upgrade, pm2 restart]
B --> C[smoke-test<br/>wait for X-Git-Sha,<br/>run scripts/smoke_test.sh]
C --> D[publish-api-types<br/>diff vs npm,<br/>publish if changed]
Run the smoke tests locally against any instance:
scripts/smoke_test.sh # prod
scripts/smoke_test.sh http://localhost:8000 # localdog-api/
├── app/ # Application code
│ ├── __init__.py
│ ├── main.py # FastAPI app instance
│ ├── models.py # Domain models (Status, APIResponse)
│ ├── app_config.py # FastAPI configuration
│ ├── openapi.py # OpenAPI documentation setup
│ ├── middleware/ # Middleware
│ │ ├── __init__.py
│ │ └── cors.py # CORS middleware
│ ├── routes/ # API routes
│ │ ├── __init__.py
│ │ ├── breeds.py # Breed endpoints
│ │ ├── descriptions.py # Description endpoints
│ │ ├── health.py # Health check endpoints
│ │ └── images.py # Image endpoints
│ └── services/ # Business logic
│ ├── __init__.py
│ ├── breed_service.py # Breed/image operations
│ └── description_service.py # Description operations
├── config.py # Environment configuration
├── main.py # Entry point (runs uvicorn)
├── server.py # Legacy file (deprecated)
├── env.example # Environment variables template
├── nginx.conf # Nginx configuration
└── reload-nginx.sh # Nginx reload script
- Root level: Configuration files, entry point, deployment files
app/: All application code (similar tosrc/in JavaScript projects)models.py: Pydantic models and response utilitiesroutes/: API endpoint handlersservices/: Business logic layermiddleware/: HTTP middleware (CORS, etc.)openapi.py: OpenAPI/Swagger documentation configurationapp_config.py: FastAPI application configurationmain.py: FastAPI app instance creation
make # List all targets
make install # Install dependencies (incl. dev tools)
make start # Run dev server with auto-reload
make check # Lint, format check and tests (same as CI)
make format # Format and autofix with ruff
make db-upgrade # Apply migrations
make smoke # Smoke-test prod (or URL=http://localhost:8000)python main.py # Run development server
uvicorn app.main:app --reload # Run with auto-reloadThe application will start on http://localhost:8000 (or the port specified in config.py).