Skip to content

Migrating from pocketbase-typegen

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.

Conceptpocketbase-typegenpbkit
Outputone pocketbase-types.tstypes.gen.ts, client.gen.ts, sdk.gen.ts
ConfigurationCLI flagsa pbkit.config.ts file
Data accesscast pb to TypedPocketBase, call pb.collection(...)import generated functions
Expanding relationsmanual generic parametersinferred from the expand string — typed record.expand.x

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.

Terminal window
bun remove pocketbase-typegen
bun add -d @karnak19/pbkit
bun add pocketbase

Step 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:

pbkit.config.ts
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 PbkitConfig

Use the table below to translate your input source:

pocketbase-typegen flagpbkit 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.dbnot 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’s token field is the value to use. A token is only needed for non-public schemas — if GET /api/collections is 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"
}
}
Terminal window
bunx pbkit generate

This writes three files into ./src/generated:

  • types.gen.ts — TypeScript interfaces
  • client.gen.ts — a PocketBase client singleton
  • sdk.gen.ts — typed CRUD functions

The generated type names differ. Use this mapping to update imports:

pocketbase-typegenpbkitNotes
XxxResponseXxxRecordthe full record returned by the API
XxxRecordXxxCreate / XxxUpdatethe input shape; pbkit splits create vs. update
Collections enumCollectionName unionstring literal union instead of an enum
XxxStatusOptions enuminline string unione.g. "draft" | "published" directly on the field
BaseSystemFieldsBaseRecordbase system fields
AuthSystemFieldsAuthRecordauth-collection system fields

For example, where you previously wrote:

// before
import { ArticlesResponse, ArticlesRecord, Collections } from "./pocketbase-types"
const article: ArticlesResponse = await pb.collection("articles").getOne("id")
const draft: ArticlesRecord = { title: "Hello" }

you now write:

// after
import 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.

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-typegenpbkit
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.

pocketbase-typegen requires you to type expanded relations manually through a generic parameter:

// before
const article = await pb
.collection("articles")
.getOne<ArticlesResponse<{ author: UsersResponse }>>("RECORD_ID", { expand: "author" })
article.expand?.author.email

pbkit 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:

// after
const article = await getArticle("RECORD_ID", { expand: "author" })
article.expand?.author // typed as UsersRecord — read it directly

Multi-relations come back as arrays and nested paths nest the expand object. See Expand types for the full details.

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:

pbkit.config.ts
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.

Auth-collection methods move from pb.collection(...) to dedicated functions:

// before
await pb.collection("users").authWithPassword("user@example.com", "password")
// after
import { 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.

  1. Run bunx pbkit generate and confirm the three files appear in your output directory.
  2. Run your type checker (bunx tsc --noEmit) and resolve any remaining imports of the old pocketbase-types file.
  3. Delete the old pocketbase-types.ts.