pocketbase-typegen

repository·main·Indexed 21 days ago

https://github.com/patmood/pocketbase-typegen

A tool for generating TypeScript definitions from a PocketBase schema to enable end-to-end type safety. It provides a CLI and programmatic API to produce types for collections, including record and response types, select field enums, and helpers for create and update operations. Supports multiple schema sources including direct URLs, superuser credentials, auth tokens, local SQLite databases, and JSON schema exports.

Tokens
7.2K
Snippets
29
Records
31
Agent score
73%

What's inside pocketbase-typegen

  1. Understand the generated output structure

    main

    The generated pocketbase-types.ts file contains several key entities:

    • Collections: A constant object mapping PascalCase names to collection IDs, with a companion union type.
    • [CollectionName]Record: A type for each collection (e.g., ProfilesRecord).
    • [CollectionName]Response: A response type for each collection (e.g., ProfilesResponse) including system fields.
    • [CollectionName][FieldName]Options: If a collection has a select field, a const object and union type of the options are generated.
    • CollectionRecords: A type mapping each collection name to its record type.
    • CollectionResponses: A type mapping each collection name to its response type.
    • TypedPocketBase: A type for use with a type-asserted PocketBase instance.
  2. Quickstart: Generate PocketBase TypeScript types

    main

    Use npx pocketbase-typegen to generate TypeScript definitions from your PocketBase schema. By providing a URL and superuser credentials (email/password or token), the tool will produce a pocketbase-types.ts file containing types for all your collections.

    npx pocketbase-typegen --url https://myproject.pockethost.io --email admin@myproject.com --password 'secr3tp@ssword!'
  3. Generate types using different sources

    main

    You can trigger type generation using several different input methods:

    • Auth Token: Use --token <token> (obtain via PocketBase Dashboard > Collections > _superusers > Impersonate).
    • Environment Variables: Use --env to load PB_TYPEGEN_URL, PB_TYPEGEN_EMAIL, PB_TYPEGEN_PASSWORD, or PB_TYPEGEN_TOKEN from a .env file.
    • Local SQLite DB: Use --db <path> to point directly to your data.db file.
    • JSON Schema: Use --json <path> to point to a JSON schema exported from the PocketBase admin UI.
    # Auth Token example
    npx pocketbase-typegen --url https://myproject.pockethost.io --token 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9'
    
    # Database example
    npx pocketbase-typegen --db ./pb_data/data.db
    
    # JSON example
    npx pocketbase-typegen --json ./pb_schema.json
  4. Automate type generation with PocketBase hooks

    main

    You can automate type generation so that it runs every time a collection is created, updated, or deleted. Create a generateHooks.pb.js file in your pb_hooks directory.

    /// <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)
  5. How PocketBase field types map to TypeScript

    main

    The library uses a mapping system to convert PocketBase field types into their TypeScript equivalents. Basic types like bool, email, text, url, password, and number map to standard primitives. Complex types are determined by the field schema:

    • date / autodate: Maps to specific date string types.
    • editor: Maps to an HTML string type.
    • geoPoint: Maps to a specific GeoPoint type.
    • file: Returns a file name string, or an array of file name strings if maxSelect > 1.
    • json: Returns null | [GenericType] based on the field name.
    • relation: Returns a record ID string, or an array of record ID strings if maxSelect > 1.
    • select: Returns a specific Enum type (generated from the field values) or a string, and handles arrays if maxSelect > 1.

    Note: The user type is considered deprecated as PocketBase v0.8+ handles user relations via standard relations.

  6. Understand the Collection and Field schema structure

    main

    The generator uses a schema model representing PocketBase collections and their fields. This structure is used to map database entities to TypeScript types.

    CollectionRecord

    Represents a PocketBase collection. It includes:

    • id: Unique identifier.
    • type: The collection type ("base", "auth", or "view").
    • name: The collection name.
    • fields: An array of FieldSchema objects.
    • listRule, viewRule, createRule, updateRule, deleteRule: API access rules (strings or null).

    FieldSchema

    Represents an individual field within a collection. It includes:

    • id: Unique identifier.
    • name: The field name.
    • type: One of "text", "file", "number", "bool", "email", "url", "date", "autodate", "select", "json", "relation", "user", "editor", or "geoPoint".
    • system: Boolean indicating if it is a system field.
    • required: Boolean indicating if the field is mandatory.
    • unique: Boolean indicating if the field must be unique.
    • RecordOptions: Additional constraints like min, max, pattern, or values (for select fields).
    export type FieldSchema = {
      id: string
      name: string
      type: "text" | "file" | "text" | "number" | "bool" | "email" | "url" | "date" | "autodate" | "select" | "json" | "relation" | "user" | "editor" | "geoPoint"
      system: boolean
      required: boolean
      unique: boolean
    } & RecordOptions
    
    export type CollectionRecord = {
      id: string
      type: "base" | "auth" | "view"
      name: string
      system: boolean
      fields: FieldSchema[]
      listRule: string | null
      viewRule: string | null
      createRule: string | null
      updateRule: string | null
      deleteRule: string | null
    }
  7. Type JSON fields and expanded relations

    main

    For advanced scenarios, you can pass generic arguments to your Response types to provide type safety for JSON fields and expanded relations (using PocketBase's expand feature).

    import { Collections, CommentsResponse, UsersResponse } 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" })
    
    // Access expanded relation with type safety
    result.expand.user.username
  8. Use TypedPocketBase for automatic collection typing

    main

    Instead of manually typing every request, you can cast your PocketBase instance to TypedPocketBase. This enables automatic type inference for all collection operations based on the generated types.

    import { TypedPocketBase } from "./pocketbase-types"
    
    const pb = new PocketBase("http://127.0.0.1:8090") as TypedPocketBase
    
    // Results in inferred response types (e.g., TaskResponse, PostResponse)
    await pb.collection("tasks").getOne("RECORD_ID")
    await pb.collection("posts").getOne("RECORD_ID")
  9. Use generic types for specific requests

    main

    If you prefer not to use TypedPocketBase, you can use the generated Collections enum and specific response types manually in your calls.

    import { Collections, TasksResponse } from "./pocketbase-types"
    
    await pb.collection(Collections.Tasks).getOne<TasksResponse>("RECORD_ID")
  10. Type Create and Update operations

    main

    The generated types include Create<T> and Update<T> helpers to ensure your payload matches the collection schema during write operations.

    import { Collections, Create, Update } from "./pocketbase-types"
    
    // Create
    const newUser: Create<Collections.Users> = {
      name: "Name",
      username: "username",
      password: "password",
      passwordConfirm: "password",
      email: "user@mail.com",
      emailVisibility: true,
      verified: false,
    }
    await pb.collection(Collections.Users).create(newUser)
    
    // Update
    const updatedUser: Update<Collections.Users> = {
      name: "Updated name",
      email: "user@email.com",
      verified: false,
    }
    await pb.collection(Collections.Users).update("RECORD_ID", updatedUser)
  11. Use the programmatic API

    main

    You can import generateFromSchema to use the tool as a library within Vite plugins, build hooks, or custom scripts.

    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)
    
    // Optional: omit TypedPocketBase type
    // generateFromSchema(collections, { sdk: false })
  12. Configure Pocketbase Typegen via Environment Variables

    main

    If you use the --env flag (or if it is enabled by default), the CLI will look for the following environment variables to resolve the schema source. You can optionally provide a path to a directory containing a .env file using --env <dir>.

    Required variables:

    • PB_TYPEGEN_URL: The URL of your PocketBase instance.

    Authentication variables (choose one):

    • PB_TYPEGEN_TOKEN: An auth token for a superuser.
    • PB_TYPEGEN_EMAIL and PB_TYPEGEN_PASSWORD: Superuser credentials.
    # Example: Using a .env file in a specific directory
    pocketbase-typegen --env ./config