pocketbase-typegen: turning a PocketBase schema into TypeScript types
Typescript generation for pocketbase records
At a glance
- What is it?
- pocketbase-typegen reads a PocketBase schema, whether from a hosted instance, a local SQLite file or an exported JSON schema, and writes a TypeScript file with record, response, create and update types for every collection. It is a small CLI with a programmatic API, and the types are only as good as the schema you feed it.
- Who is it for?
- Adopt pocketbase-typegen if your frontend talks to PocketBase and you want collection access typed end to end without hand writing the model types every time somebody edits a collection. Do not adopt it if your frontend talks to a REST API you control, since you can generate those types from an OpenAPI schema with tooling built for that.
- Can I use it commercially?
- Not without permission. GitHub finds no licence file in the repository, and without a licence all rights are reserved by default: you may read the code but not reuse it. Check the README, or ask the authors, before using it.
- Is it still maintained?
- Yes. The repository last received commits 83 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 25, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem: PocketBase records arrive untyped
PocketBase is a backend whose schema is defined through an admin interface rather than in code, and the frontend side of that arrangement is a typing problem. A collection has a name, a set of fields with types, and relations to other collections, and the API returns records that carry those fields plus a set of system fields. The official JavaScript SDK is generic over the collection name, so a call to getOne returns a record service with whatever shape the server happened to send, and every field access on the result is untyped at the call site.
pocketbase-typegen solves exactly that. It generates TypeScript definitions from your pocketbase.io schema, and the quickstart says it produces types for all your PocketBase collections to use in your frontend typescript codebase.
The scope is worth being precise about. This is a code generator, not a runtime library, and it does not change how PocketBase works. It reads a schema and writes a .ts file. Nothing in your application depends on the generator at runtime, which means the generated file is checked-in source that you own, and the tool is something you run rather than something you deploy.
The package is published to npm as pocketbase-typegen, with the homepage pointing at the npm package page. The repository is patmood/pocketbase-typegen, the primary language is TypeScript, and the description reads Generate typescript definitions from your pocketbase.io schema.
The target user is a frontend developer working against a PocketBase backend who wants the same type safety they would have with a typed API client, without maintaining the model definitions by hand every time somebody edits a collection.
Three ways to feed it a schema
The generator accepts the schema from three sources, and which one you use determines how much of your infrastructure the tool needs to reach.
The first is a hosted instance over its API. The quickstart runs `npx pocketbase-typegen --url https://myproject.pockethost.io --email [email protected] --password 'secr3tp@ssword!'`, and you authenticate as a PocketBase superuser, either with email and password or with an auth token. The token variant is `npx pocketbase-typegen --url https://myproject.pockethost.io --token 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9'`, and the README explains where to get such a token, either via the impersonate API or from the Dashboard, Collections, _superusers, select a superuser, then the Impersonate dropdown option.
The second source is the SQLite database file directly, which is the option for a local or self hosted PocketBase. The command is `npx pocketbase-typegen --db ./pb_data/data.db`, and it is also the option the automatic generation hook uses, because a hook runs inside PocketBase and has direct access to the data directory.
The third source is a JSON schema exported from the PocketBase admin dashboard, with `npx pocketbase-typegen --json ./pb_schema.json`. That one needs no credentials and no running server, which makes it the easiest option for a CI job that regenerates types from a committed schema export.
There is also an environment variable mode, `--env`, which reads configuration from a .env file, and it supports environment specific files such as .env.local through dotenv-flow. The variables are:
PB_TYPEGEN_URL=https://myproject.pockethost.io
[email protected]
PB_TYPEGEN_PASSWORD=secr3tp@ssword!
PB_TYPEGEN_TOKEN=eyJhbGciOiJI...ozhyQVfYm24The dependency list in the package.json explains the implementation. better-sqlite3 is what reads the local database, commander is the CLI parser, and dotenv-flow handles the environment files. The tool is small because it does one thing.
The full option list is also useful, and it is worth skimming once:
Options:
-V, --version output the version number
-u, --url <url> URL to your hosted pocketbase instance. When using this options you must also provide email and
password options or auth token option.
--email <email> Email for a pocketbase superuser. Use this with the --url option.
-p, --password <password> Password for a pocketbase superuser. Use this with the --url option.
-t, --token <token> Auth token for a pocketbase superuser. Use this with the --url option.
-d, --db <path> Path to the pocketbase SQLite database.
-j, --json <path> Path to JSON schema exported from pocketbase admin UI.
--env [dir] Use environment variables for configuration. Add PB_TYPEGEN_URL, PB_TYPEGEN_EMAIL, PB_TYPEGEN_PASSWORD
to your .env file. Optionally provide a path to a directory containing a .env file (default: true)
-o, --out <path> Path to save the typescript output file. (default: "pocketbase-types.ts")
--no-sdk Removes the pocketbase package dependency. A typed version of the SDK will not be generated.
-h, --help display help for commandWhat the generated file actually contains
The output is a single TypeScript file, pocketbase-types.ts by default, and the README enumerates what is in it.
There is a Collections const object mapping PascalCase names to collection IDs, with a companion union type. There is one Record type for each collection, for example a ProfilesRecord. There is one Response type for each collection, for example a ProfilesResponse, which includes the system fields and is what is returned from the PocketBase API. And there are per field option types, named like a collection and field combination, for a collection that contains a select field with a set value.
The Collections map is what makes the typed SDK work. With it, the collection name is checked at compile time, and a typo in a collection name is a type error rather than a failed request at runtime.
There are two ways to consume it. The first is the generated TypedPocketBase type, which makes collections automatically typed:
import { TypedPocketBase } from "./pocketbase-types"
const pb = new PocketBase("http://127.0.0.1:8090") as TypedPocketBase
await pb.collection("tasks").getOne("RECORD_ID") // -> results in TaskResponse
await pb.collection("posts").getOne("RECORD_ID") // -> results in PostResponseThe second is explicit generics per request, which is the path when you do not want to cast the whole client:
import { Collections, TasksResponse } from "./pocketbase-types"
await pb.collection(Collections.Tasks).getOne<TasksResponse>("RECORD_ID") // -> results in TaskResponseCreate and update operations are typed as well, with Create and Update types over a collection:
import { Collections, Create, Update } from "./pocketbase-types"
// Create
const newUser: Create<Collections.Users> = {
name: "Name",
username: "username",
password: "password",
passwordConfirm: "password",
email: "[email protected]",
emailVisibility: true,
verified: false,
}
await pb.collection(Collections.Users).create(newUser)That Create type is the one that pays for itself. A new required field added in the PocketBase admin UI produces a type error at every create call site once the types are regenerated, which is a compile time reminder that the schema moved under you.
JSON fields and expanded relations need explicit types
The advanced usage is where the generator's limits show, and it is worth understanding before you rely on it.
PocketBase has two field types the generator cannot infer. JSON fields have an arbitrary shape, and relations come back expanded or not depending on the request. The generator handles both by making them generic parameters you fill in yourself.
The Response types take generic arguments for a JSON field's type and for the expanded relations, and the record type in the README shows a JSON field typed as null or the type parameter and a relation field typed as RecordIdString:
import { Collections, CommentsResponse, UserResponse } from "./pocketbase-types"
type Metadata = {
likes: number
}
type Expand = {
user: UsersResponse
}
const result = await pb
.collection(Collections.Comments)
.getOne<CommentsResponse<Metadata, Expand>>("RECORD_ID", { expand: "user" })
// Now you can access the expanded relation with type safety and hints in your IDE
result.expand.user.usernameThe default for the JSON generic is unknown, which is the honest default, since the generator reads the schema and the schema does not describe the inside of a JSON field. The Expand generic is the same idea for relations, and the expand parameter has to match the one passed in the request options, which means the type and the runtime call are two places that can drift apart.
That drift is the real failure mode of this tool. The generated types are only correct as long as the schema they came from is the schema the server runs, and the expand arguments are only correct as long as the generic matches the option. A schema change without a regeneration produces type errors, which is the good case, and a regeneration against the wrong schema produces confidently wrong types, which is the bad one.
The tool also has a `--no-sdk` option that removes the pocketbase package dependency, so a typed version of the SDK is not generated. That is what you want when the SDK is not a dependency of the project consuming the types.
Hooking generation into collection changes
The most interesting feature is the automatic generation path, because it removes the human step that usually breaks.
PocketBase hooks can be used to generate new types every time a collection is created, updated or deleted. The mechanism is a JavaScript file, generateHooks.pb.js, placed in a directory called pb_hooks alongside your pocketbase executable:
/// <reference path="../pb_data/types.d.ts" />
const generateTypes = (e) => {
console.log("Collection changed - Running type generation...")
const cmd = $os.cmd(
"npx",
"pocketbase-typegen",
"--db",
"pb_data/data.db",
"--out",
"../client/src/pocketbase-types.ts"
)
const result = toString(cmd.output())
console.log(result)
e.next()
}
onCollectionAfterCreateSuccess(generateTypes)
onCollectionAfterUpdateSuccess(generateTypes)
onCollectionAfterDeleteSuccess(generateTypes)The three registration functions are the ones that matter, and registering on the success variants rather than the general ones means the types are regenerated only when a change actually completes.
Two things about the hook are worth knowing. It shells out to npx, so the machine running PocketBase needs Node available, and the npx call will resolve the package at run time. And it writes to a path relative to the PocketBase working directory, here ../client/src/pocketbase-types.ts, so the layout of your project on disk is part of the configuration.
For a project where the frontend is a separate repository or a separate deployed bundle, this hook is the wrong place to run. The alternative is a scheduled job or a CI step, which is what the JSON schema export mode is for.
There is also a programmatic API for everything the CLI does. The README describes it as useful for Vite plugins, build hooks or watch mode generators:
import { generateFromSchema } from "pocketbase-typegen"
// Load your schema however you like — from a JSON file, API, etc.
const collections = JSON.parse(await fs.readFile("./pb_schema.json", "utf8"))
const typeDefinitions = generateFromSchema(collections)And generateFromSchema takes an optional second argument, so you can omit the TypedPocketBase type with `generateFromSchema(collections, { sdk: false })`.
The last convenience is a package.json script, which is what most projects end up with:
"scripts": {
"typegen": "pocketbase-typegen --env",
},Where this generator falls short
The limits are structural, and knowing them saves an afternoon.
The first is that the generator can only describe what the schema describes. A select field produces a union of its configured values, and a relation field produces a record id. A JSON field produces unknown. Any behaviour that lives in a PocketBase hook, a server side function or an application layer above the database is invisible to it, and the types will be correct and incomplete at the same time.
The second is the schema drift problem, and the hook is only a partial answer. A hook regenerates types when a collection changes on that PocketBase instance, which covers the developer editing a collection in the admin UI. It does not cover a schema that arrived by an import, by a migration, or from a different environment, and it does not cover a frontend repository that pulls the types rather than sharing a filesystem with the server. In those setups you want the generated file committed and a CI check that regenerates it and fails when the result differs, and the JSON export mode is the one to use there.
The third is the credentials question. The URL mode needs a superuser password or an auth token, and a superuser token is the most privileged credential PocketBase has. Putting it in a .env file read from the environment is reasonable, but in the URL mode the tool is talking to a hosted instance over the network with a superuser credential, and that is a meaningful thing to have in a build step.
The fourth is that the tool is coupled to PocketBase. If your backend is a REST API you control, generating types from an OpenAPI schema is the closer fit, because that schema describes the wire format directly rather than the storage layout. PocketBase's schema is a storage layout that happens to drive the API, and the generator maps one to the other by convention, which is exactly the kind of mapping that can miss a case.
The maintenance picture is good but small. The repository is not archived and the last push was on 2026-08-04, with releases v1.5.0 from 2026-04-23, v1.4.1 from 2026-04-21 and v1.4.0 from 2026-04-17. The package is on version 1.5.0, requires Node 18 or newer, and the scripts show a proper release pipeline, with prepublishOnly running the type check, the tests and the build, and the tests themselves running under vitest with coverage. There is a Dockerfile for the integration tests that downloads a real PocketBase binary, version 0.37.3, and runs against it, which is the kind of setup that catches the drift problems rather than hiding them.
One licensing detail to check. The package.json declares the license as ISC, while the repository licence is recorded as unknown with no LICENSE file in the top level listing. For a code generator whose output you commit into your own repository, the licence of the generator itself rarely matters, but if you redistribute it as part of a larger tool, read the terms.
Editorial conclusion
Adopt pocketbase-typegen if your frontend talks to PocketBase and you want collection access typed end to end without hand writing the model types every time somebody edits a collection. Do not adopt it if your frontend talks to a REST API you control, since you can generate those types from an OpenAPI schema with tooling built for that. Verify first that the schema you point it at matches the schema your server runs, that the generated file is committed in a way that fails the build when it drifts, and that select and relation fields are configured so the response types carry useful information.
Frequently asked questions
How do I generate TypeScript types from my PocketBase schema?
Run `npx pocketbase-typegen --url https://myproject.pockethost.io --email [email protected] --password 'secr3tp@ssword!'` against a hosted instance, point it at a local database with `npx pocketbase-typegen --db ./pb_data/data.db`, or use `npx pocketbase-typegen --json ./pb_schema.json` with a schema exported from the admin dashboard.
What does the generated pocketbase-types.ts file contain?
A Collections const object mapping PascalCase names to collection IDs with a companion union type, one Record type per collection, one Response type per collection that includes the system fields, and option types for select fields with a set value.
How do I type JSON fields and expanded relations with pocketbase-typegen?
The Response types take generic arguments for the JSON field type and for the expanded relations, for example getOne<CommentsResponse<Metadata, Expand>>("RECORD_ID", { expand: "user" }). The JSON generic defaults to unknown because the schema does not describe the contents of a JSON field.
Can pocketbase-typegen regenerate types automatically when a collection changes?
Yes. PocketBase hooks can call the generator, and the README shows a generateHooks.pb.js file placed in a pb_hooks directory that registers onCollectionAfterCreateSuccess, onCollectionAfterUpdateSuccess and onCollectionAfterDeleteSuccess to run the generator against pb_data/data.db.
Can I use pocketbase-typegen as a library instead of a CLI?
Yes. The package exports generateFromSchema, which takes the collections and returns the type definitions, and the README describes this as useful for Vite plugins, build hooks or watch mode generators. An optional second argument such as { sdk: false } omits the TypedPocketBase type.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/patmood-pocketbase-typegen)Was this useful?