Connecting is a good start, but let’s actually store some orders. In Trellis, you don’t provision databases or juggle connection strings in service code. You ask for a Key-Value bucket in your contract, accept the desired deployment authority change, and Trellis provisions it during authority reconciliation.
First, define what an Order looks like and add a service-owned orders KV resource.
Create schemas.ts:
import { type Static, Type } from "@sinclair/typebox";
export const Order = Type.Object({
orderId: Type.String(),
customerId: Type.String(),
status: Type.Union([
Type.Literal("pending"),
Type.Literal("shipped"),
Type.Literal("failed"),
]),
items: Type.Array(Type.Object({
productId: Type.String(),
quantity: Type.Number({ minimum: 1 }),
})),
createdAt: Type.String({ format: "date-time" }),
});
export type Order = Static<typeof Order>; Next, open contracts/orders_service.ts and update two things:
- Pass the
Orderschema into the contract registry soref.schema("Order")can reference it. - Add the KV resource with the required
schemafield referencing that name.
import * as schemas from "../schemas.ts";
export const ordersService = defineServiceContract(
{ schemas },
(ref) => ({
// ...
resources: {
kv: {
orders: {
purpose: "Store order records",
schema: ref.schema("Order"),
history: 1,
ttlMs: 0,
},
},
},
}),
); The default required: true means deploy fails if Trellis cannot provision or bind the bucket.
Finally, back in main.ts, the KV store is ready to use after TrellisService.connect(...). The service.kv.orders handle was already resolved and opened during connection. You can use it directly without any extra setup:
// After TrellisService.connect(...)
// service.kv.orders is already a fully typed TypedKV<Order>
const startupEntry = await service.kv.orders.get("some-key").take(); Do not fetch or pass binding data yourself. Service resource bindings are
resolved by the Trellis runtime and exposed through the connected service object. Handler code accesses the same bindings through client.kv.orders in their method parameters.
The
.take()/isErr(...)pattern shows up throughout Trellis. Rather than throwing exceptions, Trellis returnsResult<T, E>values. Call.take()to get either the success value or error value, then useisErr(value)before using the value.
Because we changed the contract, review the pending authority change in Console. Accepting it approves the desired resource boundary and schedules reconciliation, which provisions the required new bucket. Open Admin → Services, select orders-service, inspect the Authority review required delta, and accept or reject it.
For local development or automation, the Trellis CLI can decide the same pending request:
trellis svc orders-service authority plan list --state pending
trellis svc orders-service authority plan show <plan-id>
trellis svc orders-service authority accept-update <plan-id>