Every Shaperail project has a single configuration file named shaperail.config.yaml in the project root. This file defines the project configuration surface for the generated app and optional runtime features.
Minimal config
The only required field is project:
project: my-app
With nothing else specified, the server starts on port 3000 with automatic worker detection and no database, cache, or auth.
Full annotated example
project: my-api
port: 8080
workers: 4
databases:
default:
engine: postgres
# `.env` sets DATABASE_URL, which overrides the fallback below.
# Edit `.env` (not this file) for local connection strings.
url: ${DATABASE_URL:postgresql://localhost/my_api_db}
pool_size: 20
cache:
type: redis
url: redis://${REDIS_HOST:localhost}:6379
auth:
provider: jwt
secret_env: JWT_SECRET
expiry: 24h
refresh_expiry: 30d
storage:
provider: s3
bucket: my-bucket
region: us-east-1
logging:
level: info
format: json
otlp_endpoint: http://localhost:4317
events:
subscribers:
- event: "user.created"
targets:
- type: webhook
url: "https://example.com/hooks/user-created"
- type: job
name: send_welcome_email
- type: channel
name: notifications
room: "org:{org_id}"
- type: hook
name: validate_org
webhooks:
secret_env: WEBHOOK_SECRET
timeout_secs: 30
max_retries: 3
inbound:
- path: /webhooks/stripe
secret_env: STRIPE_WEBHOOK_SECRET
events: ["payment.completed", "subscription.updated"]
Section reference
project
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
project | string | yes | – | Project name. Used for logging, Docker image tags, and the generated crate name. |
port
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
port | integer | no | 3000 | TCP port the HTTP server binds to. |
workers
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
workers | "auto" or integer | no | auto | Number of Actix-web worker threads. auto uses the number of CPU cores. |
protocols (M15/M16)
Optional. List of API protocols to enable. When omitted, defaults to ["rest"].
protocols:
- rest
- graphql
- grpc
| Value | Description |
|---|---|
rest | REST API (list, get, create, update, delete) — always available when a database is configured. |
graphql | GraphQL endpoint at /graphql and Playground at /graphql/playground. Current list fields expose limit and offset; see the GraphQL guide. |
grpc | gRPC server on a separate port (default 50051). Current runtime support covers list/stream/get/create/delete plus health/reflection; see the gRPC guide. |
Only rest, graphql, and grpc are allowed. Unknown values cause a parse error.
graphql (M15)
Optional. Configures GraphQL depth and complexity limits. Only relevant when graphql is in protocols.
graphql:
depth_limit: 10
complexity_limit: 200
| Field | Type | Default | Description |
|---|---|---|---|
depth_limit | integer | 16 | Maximum query nesting depth. Queries exceeding this depth are rejected. |
complexity_limit | integer | 256 | Maximum query complexity score. Queries exceeding this are rejected. |
grpc (M16)
Optional. Configures the gRPC server. Only relevant when grpc is in protocols.
grpc:
port: 50051
reflection: true
| Field | Type | Default | Description |
|---|---|---|---|
port | integer | 50051 | Port for the gRPC server. Separate from the HTTP port. |
reflection | boolean | true | Enable gRPC server reflection for tools like grpcurl. |
databases
Migrating from v0.10: The legacy singular
database:block was removed in v0.11. Configs containing it now fail to parse withunknown field 'database'. Replace the block withdatabases.default:as shown below.
Optional. Named database connections. Every new project scaffolded by shaperail init uses this form. When omitted, no database pool is created.
The recommended form (also what shaperail init now generates):
databases:
default:
engine: postgres
# `.env` sets DATABASE_URL, which overrides the fallback below.
# Edit `.env` (not this file) for local connection strings.
url: ${DATABASE_URL:postgresql://localhost/<project>}
pool_size: 20
You must include a connection named default; migrations run against the default connection. Use ${VAR} or ${VAR:default} in URLs for environment variable interpolation.
For multi-database setups, add additional named connections:
databases:
default:
engine: postgres
url: ${DATABASE_URL}
pool_size: 20
analytics:
engine: postgres
url: postgres://user:pass@analytics-db.example.com/analytics
pool_size: 10
databases — connection options
Optional. Named database connections for multi-database projects. When set, the server uses an ORM-backed store and routes each resource to the connection named by its db: key (or default when omitted).
You must include a connection named default; migrations run against the default connection. Use ${VAR} or ${VAR:default} in URLs for environment variable interpolation.
databases:
default:
engine: postgres
url: ${DATABASE_URL}
pool_size: 20
analytics:
engine: postgres
url: postgres://user:pass@analytics-db.example.com/analytics
pool_size: 10
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| name | object | yes | – | Connection name (e.g. default, analytics). Resources select via db: <name>. |
engine | string | yes | – | One of: postgres, mysql, sqlite. |
url | string | yes | – | Connection URL (e.g. postgres://..., mysql://..., file:data.db). |
pool_size | integer | no | 20 | Maximum connections in the pool for this database. |
Supported engines:
- postgres — PostgreSQL. Full CRUD, filters, sort, pagination, migrations.
- mysql — SQL multi-db support is wired in the runtime and scaffold bootstrap.
- sqlite — SQL multi-db support is wired in the runtime and scaffold bootstrap.
MongoDB note: the core multi-db model also includes a mongodb engine and the runtime exposes Mongo-backed store primitives behind the multi-db feature, but the scaffolded bootstrap only wires SQL engines automatically today.
When databases is present, database is ignored and DATABASE_URL is only used if you reference it inside a databases.*.url value (e.g. default).
cache
Optional. When omitted, no Redis connection is created.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
type | string | yes | – | Cache backend. Use redis. |
url | string | yes | – | Redis connection URL (e.g., redis://localhost:6379). |
auth
Optional. When omitted, endpoints that declare auth will fail validation.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
provider | string | yes | – | Auth strategy. Use jwt. |
secret_env | string | yes | – | Name of the environment variable holding the signing secret. |
expiry | string | yes | – | Token lifetime (e.g., 24h, 60m). |
refresh_expiry | string | no | – | Refresh token lifetime (e.g., 30d). Omit to disable refresh tokens. |
Current limitation: the scaffolded app currently reads JWT settings from the JWT_SECRET environment variable only and uses built-in 24h / 30d defaults. The auth: block is parsed and validated, but the generated bootstrap does not yet consume secret_env, expiry, or refresh_expiry automatically.
storage
Optional. When omitted, file upload endpoints are unavailable.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
provider | string | yes | – | Storage backend: s3, gcs, or local. |
bucket | string | no | – | Bucket or container name. Required for s3 and gcs. |
region | string | no | – | Cloud region (e.g., us-east-1). Required for s3. |
logging
Optional. Defaults to info-level JSON logs with no OTLP export.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
level | string | no | info | Log level: debug, info, warn, or error. |
format | string | no | json | Output format: json or pretty. |
otlp_endpoint | string | no | – | OpenTelemetry collector endpoint (e.g., http://localhost:4317). Omit to disable trace export. |
events
Optional. Controls event subscribers, outbound webhook settings, and inbound webhook declarations.
Current limitation: the generated scaffold creates an EventEmitter, but it does not automatically start worker handlers for subscriber targets or register inbound webhook routes.
events.subscribers
A list of event routing rules. Each entry maps an event name to one or more targets.
events:
subscribers:
- event: "user.created"
targets:
- type: job
name: send_welcome_email
- type: webhook
url: "https://example.com/hooks/user-created"
- type: channel
name: notifications
room: "org:{org_id}"
- type: hook
name: validate_org
| Field | Type | Required | Description |
|---|---|---|---|
event | string | yes | Event name pattern (e.g., user.created, *.deleted). |
targets | list | yes | One or more dispatch targets. |
Each target has a type field that determines the remaining fields:
| Target type | Fields | Description |
|---|---|---|
job | name | Enqueue a background job by name. |
webhook | url | POST to an external URL. |
channel | name, room (optional) | Broadcast to a WebSocket channel. room scopes the broadcast. |
hook | name | Execute a server-side event handler function by name. |
Note: the hook event target type in subscriber configuration is separate from endpoint-level business logic. For synchronous request-lifecycle logic (input validation, response enrichment), use controller: on endpoints — see Controllers.
events.webhooks
Global settings for outbound webhook delivery helpers.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
secret_env | string | no | WEBHOOK_SECRET | Environment variable holding the HMAC signing secret. |
timeout_secs | integer | no | 30 | HTTP timeout in seconds per delivery attempt. |
max_retries | integer | no | 3 | Maximum retry attempts for failed deliveries. |
events.inbound
A list of inbound webhook endpoints for shaperail_runtime::events::configure_inbound_routes(...).
| Field | Type | Required | Description |
|---|---|---|---|
path | string | yes | URL path (e.g., /webhooks/stripe). |
secret_env | string | yes | Environment variable holding the verification secret. |
events | list of strings | no | Event names this endpoint accepts. Empty means all events. |
These entries are parsed by the config layer, but the scaffolded app does not call the inbound-route helper automatically.
Environment variable interpolation
Use ${VAR} to inject an environment variable at parse time. Use ${VAR:default} to provide a fallback when the variable is unset.
project: ${APP_NAME:my-app}
databases:
default:
engine: postgres
url: ${DATABASE_URL:postgresql://localhost/${DB_NAME}}
pool_size: 20
Rules:
${DB_NAME}– ifDB_NAMEis not set, the parser returns an error naming the missing variable.${DB_HOST:localhost}– ifDB_HOSTis not set,localhostis used.${}– empty placeholders are rejected.- Unterminated
${...without a closing}is rejected.
Interpolation happens before YAML parsing, so the substituted value becomes part of the raw YAML text.
Workspace configuration (M17)
Multi-service projects use shaperail.workspace.yaml instead of (or alongside) the per-service shaperail.config.yaml. See Multi-service workspaces for the full format reference, including service definitions, shared config, and saga files.
Validation rules
Shaperail rejects invalid configuration at startup with a clear error message.
projectis required. Omitting it produces a “missing field” error.- Unknown fields are rejected. Every section uses
deny_unknown_fields. A typo likedatabse:instead ofdatabases:produces an “unknown field” error listing the valid alternatives. - Type mismatches fail. Setting
port: "not-a-number"orworkers: []produces a deserialization error. - Missing env vars fail. A
${VAR}reference with no default and no matching environment variable halts parsing with a message naming the variable.