Getting Started with the API
How to configure and run the Go API server locally.
Prerequisites
- Go 1.25 or later
- Infrastructure running (
docker compose up -d) - Monorepo dependencies installed (
pnpm install)
Configuration
The API loads configuration from environment variables. Each component defines its own config struct and parses only the variables it needs at startup.
Setting up your .env
Copy the example file:
cp apps/api/.env.example apps/api/.env
Edit apps/api/.env with your local values. The .env file is gitignored and loaded automatically on startup — real environment variables always take precedence.
Environment variables
Common
These are shared across all components via config.Base:
| Variable | Default | Description |
|---|---|---|
APP_ENV | development | Environment name (development, production) |
LOG_LEVEL | info | Log level (debug, info, warn, error) |
Server
| Variable | Default | Required | Description |
|---|---|---|---|
SERVER_PORT | 4000 | No | HTTP listen port |
SERVER_READ_TIMEOUT | 5s | No | Max duration for reading request |
SERVER_WRITE_TIMEOUT | 10s | No | Max duration for writing response |
SERVER_IDLE_TIMEOUT | 120s | No | Max duration for idle connections |
SERVER_SHUTDOWN_TIMEOUT | 30s | No | Graceful shutdown deadline |
SERVER_CORS_ORIGINS | — | Yes | Comma-separated allowed CORS origins |
Authentication
JWT validation against the Identity service:
| Variable | Default | Required | Description |
|---|---|---|---|
AUTH_JWKS_URL | — | Yes | Identity service JWKS endpoint (e.g. https://main-identity.chompardo.dev/api/auth/jwks) |
AUTH_ISSUER | — | Yes | Expected token issuer (Identity service base URL) |
AUTH_AUDIENCE | — | Yes | Expected token audience (API service URL) |
Database
| Variable | Default | Required | Description |
|---|---|---|---|
DB_HOST | localhost | No | PostgreSQL host |
DB_PORT | 5432 | No | PostgreSQL port |
DB_NAME | — | Yes | Database name |
DB_USER | — | Yes | Database role |
DB_PASSWORD | — | Yes | Database password |
DB_SSLMODE | disable | No | SSL mode (disable, require, verify-full) |
DB_MAX_CONNS | 10 | No | Max pool connections |
DB_MIN_CONNS | 2 | No | Min idle connections |
DB_MAX_CONN_LIFETIME | 1h | No | Max connection lifetime |
DB_MAX_CONN_IDLE_TIME | 30m | No | Max idle time before connection is closed |
DB_AUTO_MIGRATE | false | No | Run migrations on startup (use true for local dev) |
Authorization (OpenFGA)
| Variable | Default | Required | Description |
|---|---|---|---|
FGA_API_URL | http://localhost:5080 | No | OpenFGA HTTP API URL |
FGA_STORE_ID | — | No | OpenFGA store ID (ULID). Noop client if unset |
See the Authorization Guide for setup and usage details.
How it works
Configuration uses caarlos0/env to parse environment variables into Go structs with struct tags:
type ServerConfig struct {
config.Base
Port int `env:"SERVER_PORT" envDefault:"4000"`
CORSOrigins []string `env:"SERVER_CORS_ORIGINS,required"`
}
At startup, config.LoadDotEnv() loads the .env file (if present), then each component calls config.MustParse[T]() to parse its own config. Missing required variables cause a panic with a clear error message.
Adding config to a module
When a module needs configuration, add a config.go in the module root:
// internal/modules/recipe/config.go
package recipe
import "github.com/marcuslindfeldt/chompardo/apps/api/internal/config"
type Config struct {
config.Base
MaxResults int `env:"RECIPE_MAX_RESULTS" envDefault:"50"`
}
Parse it in the module's Register() or initialization function:
func Register(r chi.Router) {
cfg := config.MustParse[Config]()
// use cfg...
}
Database Migrations
Before running the API for the first time (or after pulling new migration files), run migrations:
cd apps/api && make migrate
Alternatively, set DB_AUTO_MIGRATE=true in your .env to run migrations automatically on startup. This is convenient for local development but should be disabled in production — use the standalone cmd/migrate binary instead to avoid race conditions when multiple instances start simultaneously.
See the Database Guide for more details on writing migrations and queries.
Running the API
pnpm dev --filter=api
Or directly with Go:
cd apps/api
go run ./cmd/api
The server starts on http://127.0.0.1:4000 (or https://main-api.chompardo.dev via Traefik).
Verify it's running
curl http://localhost:4000/healthz
# ok
curl http://localhost:4000/openapi.yaml
# full OpenAPI spec
Code generation
The API uses an OpenAPI-first workflow. After changing any openapi.yaml spec:
pnpm generate --filter=api
This bundles all module specs and regenerates Go server code. See Add an Endpoint and Add a Module for details.