Project structure
What lives in your Vadyl project — entities, handlers, workflows, agents, connections, edge — and how Vadyl discovers it all.
Vadyl projects follow a convention-based layout. There's no framework boilerplate — every directory holds one kind of canonical product backend asset, and Vadyl discovers them automatically from the source tree at build time.
The layout
my-app/
├── vadyl.config.ts # bindings, deployments, environments
├── schema/ # canonical entities
│ ├── User.vadyl.ts
│ ├── Order.vadyl.ts
│ └── Product.vadyl.ts
├── src/
│ ├── handlers/ # request-time handlers
│ ├── workflows/ # durable orchestration
│ ├── events/ # event consumers
│ ├── scheduled/ # cron / interval / once jobs
│ ├── webhooks/ # inbound webhook handlers
│ ├── edge/ # edge runtime handlers
│ ├── management/ # admin / lifecycle handlers
│ ├── agents/ # AI agent definitions
│ ├── connections/ # governed connection bindings
│ └── policies/ # access policies (optional, can also be inline)
└── tests/schema/ — canonical entities
Every *.vadyl.ts file in schema/ exports one or more entity contracts. These are the canonical truth about your product's data shape, relationships, validation, and access. Vadyl compiles them into the right DDL for whichever database you bound, plus typed clients, REST routes, GraphQL types, and gRPC contracts.
src/handlers/ — request-time handlers
Core request handlers — synchronous logic invoked through the API surface. Each file maps to one operation. Handlers reach data through the runtime bridge (ctx.entities.*) — never raw SQL.
// src/handlers/orders/calculateRefund.ts
import { handler } from "@vadyl/sdk";
export default handler.core(async (ctx, input: { orderId: string }) => {
const order = await ctx.entities.Order.read(input.orderId);
return { refundable: order.status === "paid" ? order.total : 0 };
});src/workflows/ — durable orchestration
Long-running, journaled, replayable. Workflows survive process restarts because every step is durable. Use them when a single request spans multiple external calls, waits, or compensations.
src/events/ — event consumers
Subscribe to canonical entity events (order.created,user.updated) or custom platform events. Consumers are idempotent under at-least-once delivery — Vadyl guarantees it.
src/scheduled/ — cron / interval / once
Scheduled handlers fire on cron expressions, intervals, or once at a given time. Each run is durable, retryable, and observable through the same lifecycle as authored handlers.
src/webhooks/ — inbound webhook handlers
Receive signed webhooks from third parties. Vadyl handles signature verification, idempotency receipts, and retries.
src/edge/ — edge runtime handlers
Constrained handlers that run at the CDN edge. Use them for request shaping, geo routing, rate-limit prechecks, session prechecks. Edge handlers cannot read or write entities — see Authored runtime.
src/management/ — admin / lifecycle handlers
Privileged handlers that perform schema transitions, branching operations, runtime publication mutations, deployment operations, or installable surface publishing. They're gated by management authority — ordinary request handlers cannot invoke them.
src/agents/ — AI agent definitions
Native agent definitions with model bindings, plan IR, memory scope, and skill definitions. See Agents.
src/connections/ — governed connection bindings
Typed configuration for outbound integrations: HTTP APIs, OAuth, SMTP, SMS, payment, LLM, broker, search, KMS. Each connection carries secret references via the keyring, capability descriptors, egress policy, and observability hooks.
vadyl.config.ts
export default {
project: { id: "my-app", region: "us-east-1" },
bindings: {
primary: { type: "postgres" },
cache: { type: "redis" },
storage: { type: "s3" },
},
environments: ["dev", "staging", "production"],
};That's the entire conventions surface. Everything else lives where it belongs.