Carrier
Examples

Four checked-in examples.
Walk them in order and you know the language.

Every example compiles today. The repository order matches the authoring tiers: minimal → recommended → workflow → platform.

Tier 1 · minimal

hello-carrier

One file. JWT auth, typed request/response records, four routes. The safest first example for learning the language or validating AI-generated output.

What it teaches
  • single-file project shape
  • service + auth jwt
  • public and protected routes
  • typed input & response records
  • built-in auth helpers
Routes to try
  • POST /auth/register
  • POST /auth/login
  • GET /me
  • GET /health
Commands
carrier check · carrier build · carrier openapi > openapi.json
examples/hello-carrier/src/main.carrier
single file
service App {
openapi { title: "Hello Carrier API" version: "0.1.0" }
server { host: env("HOST", "0.0.0.0") port: env_int("PORT", 3000) }
}
auth jwt Auth {
issuer: env("JWT_ISSUER", "carrier")
audience: env("JWT_AUDIENCE", "carrier-users")
secret: env("JWT_SECRET", "local-dev-secret")
}
type RegisterRequest {
email: String @email
name: String @length(min: 2, max: 100)
password: String @length(min: 8)
}
type AuthTokens { access_token: String refresh_token: String }
route POST "/auth/register" public -> AuthTokens {
input: RegisterRequest
handler {
let user = auth.register(
email: input.email, name: input.name, password: input.password
)
return auth.issue_tokens(user.id)
}
}
route GET "/me" protect Auth -> MeResponse {
handler { return auth.current_user() }
}
Tier 2 · recommended production shape

inventory-control

Multi-file layout with numeric prefixes for compile order. Thin routes, business writes in actions, role-only policies, idempotent operational routes.

model · crud · policy
src/inventory/00_models.carrier
role-only policy
enum InventoryStatus { active archived }
policy InventoryItem {
write: roles [operator]
deleted: roles [operator]
}
model InventoryItem {
id: UUID
sku: String @length(min: 3, max: 80)
name: String @length(min: 3, max: 120)
warehouse: String
available_units: Int @range(min: 0, max: 1000000)
safety_stock: Int @range(min: 0, max: 1000000)
status: InventoryStatus = active
deleted_at: Time?
created_at: Time
updated_at: Time
}
action · transaction
src/inventory/20_actions.carrier
cycle-count lock
action lock_item_for_cycle_count(id: UUID) -> InventoryItem {
let actor = auth.current_user()
transaction {
let item = InventoryItem.get_for_update(id: id, scope: "all")
logs.warn("cycle count lock", {
action_name: "lock_item_for_cycle_count"
actor_id: actor.id
item_id: item.id
sku: item.sku
})
}
return InventoryItem.get(id: id, scope: "all")
}
What it teaches
  • multi-file layout (00_models, 10_types, 20_actions, 30_routes)
  • enum, model, type, crud, fn, action, custom route
  • thin routes delegating to actions
  • transaction-safe row locking
  • idempotent operational routes
  • policy declarations → manifest + generated RLS
  • structured logs, loops, array & JSON helpers
Routes to try
  • generated CRUD at /inventory/items
  • GET /inventory/items/search
  • POST /inventory/items/{id}/cycle-count-lock
  • GET /inventory/summary/{warehouse}
Tier 3 · workflow & concurrency

booking-service

Scheduling logic with tenant-aware policy, optimistic @version, jobs, events, and schedules. The workflow counterpart to the flagship example.

tenant_field · @version
src/00_models/slots.carrier
AppointmentSlot
policy AppointmentSlot {
tenant_field: org_id
read: roles [viewer, scheduler]
write: roles [scheduler]
deleted: roles [scheduler]
}
model AppointmentSlot {
id: UUID
org_id: String @index
clinician_name: String @length(min: 3, max: 120)
starts_at: Time
status: SlotStatus = open
version: Int @version
deleted_at: Time?
created_at: Time
updated_at: Time
}
job · event · schedule
src/15_async/jobs.carrier
cron → enqueue
event SlotReminderQueued {
slot_id: UUID
trigger: String
}
job send_slot_reminder(payload: Json) -> Void {
logs.info("slot reminder job", { job_name: "send_slot_reminder", payload: payload })
audit.record("send_slot_reminder", "AppointmentSlot", "scheduled", payload)
}
schedule "*/15 * * * *" run send_slot_reminder
route POST "/slots/{id}/notify"
protect StaffAuth roles [scheduler] idempotent -> Json
{
params { id: UUID }
handler {
let job_id = jobs.enqueue("send_slot_reminder", { slot_id: params.id }, delay_seconds: 5)
emit SlotReminderQueued { slot_id: params.id, trigger: "manual" }
return { job_id: job_id }
}
}
What it teaches
  • tenant-aware models and policies
  • optimistic @version
  • transaction blocks with row locking
  • idempotent write routes
  • actions for lock/review flows
  • jobs, events, schedules
  • generated count_by_* / exists_by_* helpers
Routes to try
  • generated CRUD at /slots and /bookings
  • GET /slots/search
  • POST /slots/{id}/lock
  • POST /slots/{id}/notify
  • GET /bookings/slot/{slot_id}/exists
Flagship · api/

doctor-directory

The runtime/platform showcase. Public paginated search with cache + events, admin reporting via raw SQL and DB functions, typed external client, and an idempotent admin lock route.

search · cache · emit
api/src/main.carrier
public search
route GET "/search/doctors" public -> DoctorListResponse {
query {
q: String? specialty: String? city: String? country: String?
page: Int = 1 per_page: Int = 20
sort: String = "rating" direction: String = "desc"
scope: String = "active"
}
handler {
let key = "doctor-search:" + json.stringify({
q: query.q, city: query.city, page: query.page
})
if cache.exists(key) {
return cache.get_as("DoctorListResponse", key)
}
let result = Doctor.search(
q: query.q, city: query.city,
page: query.page, per_page: query.per_page,
sort: query.sort, direction: query.direction,
scope: query.scope
)
cache.set(key, result, ttl_seconds: 120)
emit DoctorSearchCached { cache_key: key, scope: query.scope }
return result
}
}
sql.list_as
api/src/main.carrier
typed report rows
/// Admin reporting endpoint using raw SQL mapped into a typed result list
route GET "/reports/doctors/by-city" protect AdminAuth roles [admin]
-> DoctorCityReportRow[]
{
handler {
return sql.list_as(
"DoctorCityReportRow",
"select city, count(*)::bigint as total from doctors where deleted_at is null group by city order by total desc, city asc"
)
}
}
client · typed JSON
api/src/main.carrier
external integration
client WeatherApi {
base_url: env("WEATHER_API_URL", "https://api.example.com")
header "x-api-key": env("WEATHER_API_KEY", "")
timeout_ms: 3000
}
type WeatherResponse {
city: String
temperature_c: Float
condition: String
}
route GET "/weather/{city}" public -> WeatherResponse {
handler {
return WeatherApi.get("/forecast", query: { city: params.city })
}
}
What it teaches
  • Postgres-backed model + CRUD
  • paginated list & search with projection types
  • soft delete + restore with explicit scopes
  • role-aware auth and policy/RLS generation
  • action + transaction with row locking
  • route-level idempotency
  • cache + Redis, typed cache reads
  • jobs · schedules · events · audit records
  • raw SQL and DB function helpers
  • typed external JSON client
Routes to try
  • GET /doctors · /doctors/{id}
  • GET /search/doctors
  • POST /admin/doctors/{id}/lock
  • GET /reports/doctors/by-city
  • GET /reports/normalize-city/{city}
  • POST /admin/jobs/metrics
  • GET /weather/{city}
  • /health · /ready · /openapi.json · /docs