pbkit
June 3, 2026 · View on GitHub
PocketBase code generation toolkit. Introspect your collections and generate fully typed TypeScript SDKs.
What it does
Given a PocketBase schema, pbkit generates:
types.gen.ts— TypeScript interfaces for every collection (XxxRecord,XxxCreate,XxxUpdate,XxxExpand)client.gen.ts— PocketBase client singleton used by the generated SDKsdk.gen.ts— Typed CRUD functions wrapping the official PocketBase JS SDK, with typed expand parameters- Plugin output — e.g. TanStack Query options with query key helpers
Quick start
1. Install
bun add @karnak19/pbkit pocketbase
2. Create a config
// pbkit.config.ts
export default {
input: "https://my-pb.example.com", // URL → live API, or path → JSON export
output: "./src/generated",
sdk: {
baseUrl: "https://my-pb.example.com",
},
}
3. Generate
bunx pbkit generate
4. Use
import { getArticle, listArticles, createArticle } from "./generated/sdk.gen"
import type { ArticlesCreate, ArticlesRecord } from "./generated/types.gen"
// Get a single record
const article: ArticlesRecord = await getArticle("RECORD_ID")
// Expand relations with autocomplete
const articleWithAuthor = await getArticle("RECORD_ID", {
expand: "author",
})
// List with pagination
const page = await listArticles({ page: 1, perPage: 20 })
// Create
const draft: ArticlesCreate = {
title: "Hello",
status: "draft",
author: "USER_ID",
}
const newArticle = await createArticle(draft)
The generated SDK uses the client exported from client.gen.ts. Pass a client
override when needed:
import PocketBase from "pocketbase"
import { getArticle } from "./generated/sdk.gen"
const pb = new PocketBase("https://my-pb.example.com")
await getArticle("RECORD_ID", undefined, { client: pb })
Configuration
pbkit.config.ts
import type { PbkitConfig } from "@karnak19/pbkit"
export default {
// Input source
input: "https://my-pb.example.com", // Live PocketBase API
// input: { url: "https://my-pb.example.com", token: "admin-token" },
// input: "./pb_schema.json", // Exported JSON file
// Output directory
output: "./src/generated",
// Type generation options
types: {
dateStrings: true, // true → string, false → Date (default: true)
nullableFields: false, // Add | null to optional fields (default: false)
optionalFields: "required-only", // or "all"
expandDepth: 2, // Max depth for expand type unions (default: 2)
},
// SDK options
sdk: {
enabled: true, // Set false to skip SDK generation
pbImport: "pocketbase", // Custom PocketBase import path
baseUrl: "https://my-pb.example.com", // Runtime URL for client.gen.ts
typesImport: "./types.gen", // Custom types import path
},
// Collection-level configuration
collections: {
_superusers: { exclude: true }, // Skip entirely
logs: { exclude: true },
articles: { operations: { create: false, delete: false } }, // Disable specific CRUD ops
listings: { fields: { tech_spec: { type: "TechSpec", from: "$/lib/specs" } } }, // Type a json field
},
// Plugins
plugins: [],
} satisfies PbkitConfig
Generated output
Types (types.gen.ts)
// Generated by pbkit — do not edit
export interface BaseRecord {
id: string
created: string
updated: string
collectionId: string
collectionName: string
}
export interface AuthRecord extends BaseRecord {
email: string
emailVisibility: boolean
verified: boolean
}
// Per-collection types
export type ArticlesRecord = BaseRecord & {
title: string
content?: string
status: "draft" | "published" | "archived"
tags?: ("technology" | "design" | "business" | "lifestyle" | "programming")[]
author: string
views?: number
featured?: boolean
}
export type ArticlesCreate = { ... }
export type ArticlesUpdate = Partial<ArticlesCreate>
// Expand type unions (autocomplete for the expand parameter)
export type ArticlesExpand = "author" | "categories"
export type CommentsExpand = "article" | "article.author" | "article.categories" | "author"
export type CollectionName = "users" | "categories" | "articles" | "comments"
SDK (sdk.gen.ts)
// Typed CRUD per collection
getArticle(id, options?, opts?) // options.expand is typed to ArticlesExpand
getFirstArticle(filter, options?, opts?)
listArticles(params?, opts?)
getFullListArticles(params?, opts?)
createArticle(data: ArticlesCreate, opts?)
updateArticle(id, data: ArticlesUpdate, opts?)
deleteArticle(id, opts?): Promise<true>
// Auth methods for auth collections
authUserWithPassword(usernameOrEmail, password)
authUserWithOAuth2(provider, code, verifier, redirect)
requestUserPasswordReset(email)
confirmUserVerification(token)
// ...etc
TanStack Query plugin
bun add @karnak19/pbkit-tanstack
// pbkit.config.ts
import { tanstack } from "@karnak19/pbkit-tanstack"
export default {
input: "https://my-pb.example.com",
output: "./src/generated",
sdk: {
baseUrl: "https://my-pb.example.com",
},
plugins: [tanstack({ framework: "react" })],
}
Generates tanstack.gen.ts with queryOptions, mutationOptions, and query key helpers:
import {
articleOptions,
articlesOptions,
articleQueryKey,
createArticleMutationOptions,
updateArticleMutationOptions,
} from "./generated/tanstack.gen"
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"
// Query options
const { data } = useQuery(articleOptions("RECORD_ID"))
const { data } = useQuery(articlesOptions({ page: 1, perPage: 20 }))
// Manual cache invalidation with query key helpers
const queryClient = useQueryClient()
queryClient.invalidateQueries({ queryKey: articleQueryKey("RECORD_ID") })
// Mutation options
const createMut = useMutation(createArticleMutationOptions())
createMut.mutate({ title: "New article", status: "draft" })
const updateMut = useMutation(updateArticleMutationOptions())
updateMut.mutate({ id: "RECORD_ID", data: { title: "Updated" } })
CLI
pbkit generate # Generate from pbkit.config.ts
pbkit generate --watch # Watch mode (polls every 10s)
pbkit generate --config ./path/to/config.ts
pbkit --help
Development
bun install
bun run lint
bun run typecheck
bun run test
bun run build
Pull requests and pushes to main run the same checks in GitHub Actions.
Releases
This repo uses Changesets for package versioning and npm publishing.
bun run changeset
Merge the generated changeset with your feature. On main, the release workflow opens a version PR. Merging that PR publishes the public packages to npm using the NPM_TOKEN repository secret. The docs deploy workflow continues to publish apps/docs to GitHub Pages.
Programmatic API
import { parseJson, parseApi, generate, generateSdk, generateProject } from "@karnak19/pbkit"
// Parse schema
const ir = parseJson(jsonString)
const ir = await parseApi({ url: "https://my-pb.example.com", token: "admin-token" })
// Generate types
const typesCode = generate(ir, { dateStrings: true })
// Generate SDK
const sdkCode = generateSdk(ir, { typesImport: "./types.gen" })
// Full project generation
await generateProject({
input: "./schema.json",
output: "./src/generated",
})
Field type mapping
| PocketBase type | TypeScript |
|---|---|
| text, email, url, editor | string |
| number | number |
| bool | boolean |
| date | string (or Date with dateStrings: false) |
| select (single) | "a" | "b" | "c" |
| select (multiple) | ("a" | "b" | "c")[] |
| relation (single) | string |
| relation (multiple) | string[] |
| file (single) | string |
| file (multiple) | string[] |
| json | unknown |
| password | included in Create only, excluded from Record |
| autodate | excluded from Record and Create |
Packages
| Package | Description |
|---|---|
@karnak19/pbkit | Core: schema parser, type generator, SDK generator, CLI |
@karnak19/pbkit-tanstack | Plugin: TanStack Query options (queryOptions/mutationOptions) |
@karnak19/pbkit-zod | Plugin: Zod schemas from PocketBase field constraints |
License
MIT