What is Keel?
Keel brings everything you need to build for operations
- Schema-driven data modelling with managed databases with automatic migrations
- JSON and GraphQL APIs generated from your schema
- Fully extensible with TypeScript functions
- Row-level and role-based permissions
- Built-in authentication (password, Google, OIDC, SAML)
- Instant apps with a modern, fast UI designed for daily operational use
- Multi-step workflows with durable execution
- Full type safety from the database to the UI
- Barcode scanner and label printer integration
- Event subscribers for reactive logic
- Scheduled flows for background jobs
- File storage and handling
- Distributed tracing and observability
- Multiple environments (staging, production, feature branches)
- Git-based deployments with approval workflows
- Secrets and environment variable management
- Local development server with hot reload
- VSCode extension with schema validation
- Generated API clients to power custom frontend and apps
- OpenAPI and GraphQL schema exports
- Custom HTTP routes for webhooks
- Computed fields with cross-relationship aggregations
- Automatic audit trails (createdAt, updatedAt)
Schema-driven development
Your schema is the source of truth:
model Order {
fields {
customer Customer
items OrderItem[]
total Decimal @computed(SUM(order.items.price))
status Status @default(Status.Pending)
}
actions {
get getOrder(id)
list listOrders(customer.id?, status?)
create createOrder() with (customer.id, items.product.id, items.quantity)
}
@permission(roles: [Staff], actions: [get, list, create])
}This schema gives you:
- A PostgreSQL table with automatic migrations
- JSON and GraphQL APIs with authentication
- Type-safe SDK for custom functions
- Console tools for your ops team
Change the schema, push, and everything updates.
APIs without boilerplate
Every action becomes an endpoint. You get:
- JSON API with OpenAPI spec
- GraphQL API for flexible queries
- Generated clients for TypeScript, with full type safety
Authentication, validation, and permissions are handled automatically. When you need custom logic, write a TypeScript function. Keel generates the types from your schema.
import { CreateOrder, models } from "@teamkeel/sdk";
export default CreateOrder(async (ctx, inputs) => {
// Full type safety, database access, secrets
const order = await models.order.create({
customerId: inputs.customerId,
status: "Pending",
});
await sendConfirmationEmail(ctx.secrets.SENDGRID_KEY, order);
return order;
});Console for ops teams
The Console is a modern, fast UI that your ops team uses all day. It's not a basic admin panel. It's built for power users who need to move quickly through hundreds of tasks.
Change your schema and the Console updates instantly. Add a field, it appears in forms. Add an action, it gets a tool.
- Internal tools generated from your schema. Instant, rich apps with search, filtering, sorting. Forms with validation and relationship lookups. Keyboard shortcuts throughout.
- Flows for multi-step processes. Barcode scanning, approvals, document collection. Durable execution let's your build complex workflows that power anything.
- Spaces to organize tools by team. Warehouse sees receiving and stock counts. Finance sees invoicing. Each team gets a focused workspace.
- Hardware integration for printers and scanners. Print shipping labels, scan barcodes during picking.
You define the data model and business logic. The Console gives your team a professional tool to work with.
Permissions in the schema
Row-level and role-based permissions, defined declaratively:
// Staff can view and create orders
@permission(roles: [Staff], actions: [get, list, create])
// Customers can only see their own orders
@permission(expression: order.customer.identity == ctx.identity, actions: [get, list])
// Managers can update orders over $1000
@permission(
expression: ctx.identity.role == "Manager" && order.total > 1000,
actions: [update]
)Permissions apply to APIs and Console tools automatically.
Flows for complex workflows
When a single action isn't enough, build a flow:
export default ReceiveGoods(async (ctx, inputs) => {
// Scan the delivery barcode
const { deliveryId } = await ctx.ui.page({
content: [ctx.ui.inputs.scan("deliveryId", { label: "Scan delivery note" })],
});
const delivery = await models.delivery.findOne({ id: deliveryId });
// Scan each item
const { items } = await ctx.ui.page({
content: [
ctx.ui.display.table({ data: delivery.expectedItems }),
ctx.ui.inputs.scan("items", { mode: "multi" }),
],
});
// Update inventory
await ctx.step("update stock", async () => {
for (const item of items) {
await models.stockItem.update({ id: item.id }, { received: true });
}
});
// Print labels
await ctx.ui.interactive.print({
jobs: items.map((item) => ({ type: "zpl", data: item.labelZpl })),
});
});Flows have durable execution. If something fails, completed steps don't re-run.
Local development
keel runStarts a local server with your database. Console works locally too. Make changes, test flows, see traces. The VSCode extension (opens in a new tab) adds schema validation and autocomplete.