A step-by-step guide to moving an existing project from pocketbase-typegen to pbkit.
pocketbase-typegen generates a
single types file that you apply to the PocketBase JS SDK by casting your client
to TypedPocketBase. pbkit generates the same types and a ready-to-use SDK
of typed CRUD functions, so you call getArticle("id") instead of
pb.collection("articles").getOne("id").
This guide walks through the migration one piece at a time. By the end you will have removed pocketbase-typegen, generated pbkit output, and updated your call sites.
What changes
Section titled “What changes”| Concept | pocketbase-typegen | pbkit |
|---|---|---|
| Output | one pocketbase-types.ts | types.gen.ts, client.gen.ts, sdk.gen.ts |
| Configuration | CLI flags | a pbkit.config.ts file |
| Data access | cast pb to TypedPocketBase, call pb.collection(...) | import generated functions |
| Expanding relations | manual generic parameters | inferred from the expand string — typed record.expand.x |
Step 1: Swap the dependencies
Section titled “Step 1: Swap the dependencies”Remove pocketbase-typegen and add pbkit. pbkit is a build-time code generator, so
install it as a dev dependency — like pocketbase-typegen was. Keep pocketbase
itself as a regular dependency, since your app uses its SDK at runtime.
bun remove pocketbase-typegenbun add -d @karnak19/pbkitbun add pocketbaseStep 2: Replace CLI flags with a config file
Section titled “Step 2: Replace CLI flags with a config file”pocketbase-typegen is configured entirely through CLI flags, usually in a
package.json script:
// package.json (before){ "scripts": { "typegen": "pocketbase-typegen --url https://my-pb.example.com --email admin@example.com --password secret --out ./src/pocketbase-types.ts" }}pbkit reads a pbkit.config.ts instead. Create one in your project root, mapping
your old flags to config options:
import type { PbkitConfig } from "@karnak19/pbkit"
export default { input: { url: "https://my-pb.example.com", token: process.env.PB_ADMIN_TOKEN }, output: "./src/generated", sdk: { baseUrl: "https://my-pb.example.com", },} satisfies PbkitConfigUse the table below to translate your input source:
| pocketbase-typegen flag | pbkit input |
|---|---|
--url ... --email ... --password ... | { url: "...", token: "..." } |
--url ... (public schema) | "https://my-pb.example.com" |
--json ./pb_schema.json | "./pb_schema.json" |
--db ./pb_data/data.db | not supported — see note below |
Note on the token: Unlike pocketbase-typegen, pbkit does not take an email and password — it expects a superuser auth
token. Obtain one by authenticating as a superuser against your instance (POST /api/collections/_superusers/auth-with-password); the response’stokenfield is the value to use. A token is only needed for non-public schemas — ifGET /api/collectionsis publicly readable, pass just the URL. Avoid committing the token: load it from an environment variable instead.
Note on
--db: pbkit does not read the SQLite database file directly. If you were generating from--db, switch to either a live URL or an exported JSON schema (input: "./pb_schema.json").
The --out flag maps to output, but note the difference: --out was a single
file, while pbkit’s output is a directory that is cleared and rewritten
on each run. See the Configuration Reference for
all options.
Update your script to call pbkit:
// package.json (after){ "scripts": { "generate": "pbkit generate" }}Step 3: Generate
Section titled “Step 3: Generate”bunx pbkit generateThis writes three files into ./src/generated:
types.gen.ts— TypeScript interfacesclient.gen.ts— a PocketBase client singletonsdk.gen.ts— typed CRUD functions
Step 4: Update type imports
Section titled “Step 4: Update type imports”The generated type names differ. Use this mapping to update imports:
| pocketbase-typegen | pbkit | Notes |
|---|---|---|
XxxResponse | XxxRecord | the full record returned by the API |
XxxRecord | XxxCreate / XxxUpdate | the input shape; pbkit splits create vs. update |
Collections enum | CollectionName union | string literal union instead of an enum |
XxxStatusOptions enum | inline string union | e.g. "draft" | "published" directly on the field |
BaseSystemFields | BaseRecord | base system fields |
AuthSystemFields | AuthRecord | auth-collection system fields |
For example, where you previously wrote:
// beforeimport { ArticlesResponse, ArticlesRecord, Collections } from "./pocketbase-types"
const article: ArticlesResponse = await pb.collection("articles").getOne("id")const draft: ArticlesRecord = { title: "Hello" }you now write:
// afterimport type { ArticlesRecord, ArticlesCreate } from "./generated/types.gen"
const article: ArticlesRecord = await getArticle("id")const draft: ArticlesCreate = { title: "Hello", status: "draft", author: "USER_ID" }See Generated Types for the full shape of each type.
Step 5: Replace pb.collection(...) calls
Section titled “Step 5: Replace pb.collection(...) calls”This is the largest change. pocketbase-typegen relies on casting your client to
TypedPocketBase and calling methods on pb.collection(...). pbkit generates a
dedicated function per operation, so you can delete the cast entirely.
| pocketbase-typegen | pbkit |
|---|---|
pb.collection("articles").getOne(id) | getArticle(id) |
pb.collection("articles").getFirstListItem(filter) | getFirstArticle(filter) |
pb.collection("articles").getList(page, perPage) | listArticles({ page, perPage }) |
pb.collection("articles").getFullList() | getFullListArticles() |
pb.collection("articles").create(data) | createArticle(data) |
pb.collection("articles").update(id, data) | updateArticle(id, data) |
pb.collection("articles").delete(id) | deleteArticle(id) |
Before:
import PocketBase from "pocketbase"import { TypedPocketBase, Collections } from "./pocketbase-types"
const pb = new PocketBase("https://my-pb.example.com") as TypedPocketBase
const article = await pb.collection("articles").getOne("RECORD_ID")const page = await pb.collection(Collections.Articles).getList(1, 20)const created = await pb.collection("articles").create({ title: "Hello" })After:
import { getArticle, listArticles, createArticle } from "./generated/sdk.gen"
const article = await getArticle("RECORD_ID")const page = await listArticles({ page: 1, perPage: 20 })const created = await createArticle({ title: "Hello", status: "draft", author: "USER_ID" })The generated functions use the client from client.gen.ts (configured by
sdk.baseUrl) by default. To target a different instance for a single call, pass
{ client } as the last argument. See the Generated SDK
reference for full signatures.
Expanding relations
Section titled “Expanding relations”pocketbase-typegen requires you to type expanded relations manually through a generic parameter:
// beforeconst article = await pb .collection("articles") .getOne<ArticlesResponse<{ author: UsersResponse }>>("RECORD_ID", { expand: "author" })
article.expand?.author.emailpbkit infers the expanded shape from the expand string you pass — no manual
generics. record.expand.x is fully typed, just like pocketbase-typegen, while
the expand paths are computed from the schema:
// afterconst article = await getArticle("RECORD_ID", { expand: "author" })
article.expand?.author // typed as UsersRecord — read it directlyMulti-relations come back as arrays and nested paths nest the expand object.
See Expand types for the full details.
Typing json fields
Section titled “Typing json fields”pocketbase-typegen lets you type a json field through a trailing generic
parameter (e.g. ListingsResponse<..., TechSpec>). pbkit generates json fields
as unknown by default, and you map them to a concrete type in your config
instead of at every call site:
collections: { listings: { fields: { tech_spec: { type: "TechSpec", from: "$/lib/listing-specs" } }, },}listings.tech_spec is then typed as TechSpec everywhere. See
Configure collections → Type json fields.
Authentication
Section titled “Authentication”Auth-collection methods move from pb.collection(...) to dedicated functions:
// beforeawait pb.collection("users").authWithPassword("user@example.com", "password")
// afterimport { authUserWithPassword } from "./generated/sdk.gen"await authUserWithPassword("user@example.com", "password")The full set of auth, password-reset, and verification functions is listed under Generated SDK → Auth functions.
Verify the migration
Section titled “Verify the migration”- Run
bunx pbkit generateand confirm the three files appear in youroutputdirectory. - Run your type checker (
bunx tsc --noEmit) and resolve any remaining imports of the oldpocketbase-typesfile. - Delete the old
pocketbase-types.ts.
Next steps
Section titled “Next steps”- Configuration Reference — every available option
- Per-collection configuration — exclude collections or disable operations
- Add TanStack Query or Zod schemas via plugins