PocketStarter Docs

Type Safety in PocketStarter

Type safety is one of the core strengths of PocketStarter. We've built the entire boilerplate with TypeScript to catch errors early and provide an excellent developer experience.

Why Type Safety matters

Type safety helps you catch bugs before they reach production. Instead of discovering issues when users report them, TypeScript catches them during development. This means fewer runtime errors, better IDE support, and easier refactoring.

PocketBase Type Generation

PocketStarter automatically generates TypeScript types from your PocketBase schema using pocketbase-typegen. This ensures perfect type safety between your database and frontend.

How it works

The pocketbase-typegen tool reads your PocketBase schema and generates TypeScript types for all your collections, fields, and relations. When you update your schema, you need to regenerate the types:

cd frontend
pnpm run generate-types

This updates src/types/pocketbase-types.ts with your latest schema changes.

Generated types

The main types are located in src/types/pocketbase-types.ts:

// Example of generated types
export type UsersResponse = {
  id: string
  email: string
  name: string
  avatar?: string
  created: string
  updated: string
}

export type Collections = {
  users: UsersResponse
  posts: PostsResponse
  // ... other collections
}

Using Types throughout your app

Database operations

All PocketBase operations are fully typed:

import { pocketbase } from '@/lib/pocketbase'
import { Collections, UsersResponse } from '@/types/pocketbase-types'

// Fully typed - TypeScript knows the exact shape
const user: UsersResponse = await pocketbase
  .collection(Collections.Users)
  .getOne('user-id')

// TypeScript will catch if you try to access non-existent fields
console.log(user.email) // ✅ Valid
console.log(user.nonExistentField) // ❌ TypeScript error

Component props

Components use proper TypeScript interfaces:

interface UserProfileProps {
  user: UsersResponse
  onUpdate?: (user: UsersResponse) => void
}

export function UserProfile({ user, onUpdate }: UserProfileProps) {
  // TypeScript ensures user has all required fields
  return (
    <div>
      <h1>{user.name}</h1>
      <p>{user.email}</p>
    </div>
  )
}

State management

Zustand stores are fully typed for authentication state:

import { create } from 'zustand'
import { UsersResponse } from '@/types/pocketbase-types'

interface AuthStore {
  user: UsersResponse | null
  setUser: (user: UsersResponse | null) => void
}

export const useAuthStore = create<AuthStore>((set) => ({
  user: null,
  setUser: (user) => set({ user })
}))

Troubleshooting

sqlite3 binding errors

If pnpm run generate-types fails with sqlite3 binding errors (common on Node.js v22+ and Apple Silicon), run:

cd frontend/node_modules/sqlite3 && npm install --build-from-source