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 SDK
  • sdk.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 typeTypeScript
text, email, url, editorstring
numbernumber
boolboolean
datestring (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[]
jsonunknown
passwordincluded in Create only, excluded from Record
autodateexcluded from Record and Create

Packages

PackageDescription
@karnak19/pbkitCore: schema parser, type generator, SDK generator, CLI
@karnak19/pbkit-tanstackPlugin: TanStack Query options (queryOptions/mutationOptions)
@karnak19/pbkit-zodPlugin: Zod schemas from PocketBase field constraints

License

MIT