Adding a table to a SvelteKit app is usually a tour: create the collection in the database admin, write a migration so teammates get it too, write a Zod schema for validation, and generate or hand-write the TypeScript types. Four places that describe the same fields and slowly drift apart. vela generate resource treats the field list as the single source and produces the rest: a PocketBase collection, a versioned migration, a Zod schema, and synced types.
Prerequisites
A vela project with a backend. npx vela create my-app gives you one; npx vela bless adds the backend to an existing SvelteKit project (more on that here). The author:current_user field below needs authentication enabled.
Run the command
✔ Created collection articles ✔ Created migrations/1788445216_created_articles.js ✔ Created src/lib/schemas/article.ts ✔ Synced types
Three things happened:
- The
articlescollection now exists in your local PocketBase, withtitlerequired,bodyas rich text, andauthoras a relation to theuserscollection. - A migration file was written to
migrations/, so the same collection is created wherever the app is deployed or cloned. - Types were synced:
Models["articles"]andSchemas["articles"]from@velastack/pocketbasenow describe the new collection.
What was generated
The one source file is the Zod schema. The satisfies on the last line is the part that matters: it ties the schema to the synced type, so if a field changes in the database and the schema does not, the type checker says so.
import { z } from "zod";
import type { Schemas } from "@velastack/pocketbase";
export const articleSchema = z.object({
id: z.string().optional(),
collectionId: z.string().optional(),
title: z.string().nonempty(),
body: z.string().optional(),
author: z.string(),
}) satisfies Schemas["articles"];
author is a plain string in the schema because relations are stored as record ids. The current_user type also does two things outside this file: forms generated for the model skip the field and fill it from the session, and the collection’s access rules restrict rows to their owner.
Use it
The collection is reachable through the PocketBase client on locals. locals.pb is scoped to the signed-in user; locals.admin sees everything:
export const load = async ({ locals }) => {
const articles = await locals.pb.collection("articles").getFullList();
return { articles };
};The records are typed as Models["articles"], so article.title autocompletes and article.tittle does not compile. To validate input before writing, use the schema exactly as the form generator does.
vela generate scaffold takes the same field list and adds list, create, view and edit routes on top.Change the model later
The field list is not a one-shot. Once the collection exists, evolve it with a migration:
Evolving your schema with migrations covers add, remove, rename and references.
Going further
- The field syntax: every type,
!,select(...), relations and nested models likeusers/articles - Undo with
vela destroy resource articles, which drops the collection and removes the schema - Pattern page: Resource · Docs: vela generate resource


