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