npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@tigawanna/typed-pocketbase

v0.1.6

Published

Add types to the PocketBase JavaScript SDK

Readme

typed-pocketbase

npm GitHub top language GitHub Workflow Status (with branch)

Add types and Zod runtime validation to the PocketBase JavaScript SDK.

[!WARNING] This is a fork only compatible with the latest version of the PocketBase JavaScript SDK (v0.22) with PocketBase 0.23+

✨ Features

  • 🎯 TypeScript Types - Full type safety for PocketBase collections
  • 🛡️ Zod Runtime Validation - Validate data at runtime with Zod v4
  • 🎨 Pattern Filtering - Control which collections to include/exclude
  • 🔒 Security - Exclude sensitive collections from public APIs
  • Performance - Tree-shakable schemas and optimized validation
  • 🛠️ Custom Types - JSON field types that won't be overwritten
  • 📦 Batch APIs - Support for batch operations
  • 🔐 OTP & Impersonation - Support for OTP and user impersonation APIs

Installation

# npm
npm i @tigawanna/typed-pocketbase

# pnpm
pnpm i @tigawanna/typed-pocketbase

# yarn
yarn add @tigawanna/typed-pocketbase

Quick Start

1. Generate Types & Schemas

# Generate both TypeScript types and Zod schemas
npx typed-pocketbase \
  --email [email protected] \
  --password supersecret \
  --type ts,zod \
  --dir src/lib/pb

# Generate only Zod schemas
npx typed-pocketbase \
  --email [email protected] \
  --password supersecret \
  --type zod \
  --dir src/lib/pb

# With pattern filtering (exclude system collections)
npx typed-pocketbase \
  --email [email protected] \
  --password supersecret \
  --type zod \
  --ignore "^_.*" \
  --dir src/lib/pb

2. Use Generated Types & Schemas

import PocketBase from 'pocketbase';
import { TypedPocketBase } from '@tigawanna/typed-pocketbase';
import { Schema } from './pb/pb-types';
import { WatchlistCreateSchema, watchlistValidators } from './pb/pb-zod';

// Create typed client
const db = new TypedPocketBase<Schema>('http://localhost:8090');

// Runtime validation with Zod
const createData = WatchlistCreateSchema.parse({
  title: 'My Movies',
  user_id: 'user123',
  visibility: 'public'
});

// Safe validation with error handling
const result = watchlistValidators.safeCreate(formData);
if (result.success) {
  const record = await db.collection('watchlist').create(result.data);
} else {
  console.error('Validation errors:', result.error.issues);
}

CLI Options

| Option | Short | Description | Default | Example | | ------------ | ----- | ----------------------- | ----------------------- | ------------------------------- | | --url | -u | PocketBase instance URL | http://127.0.0.1:8090 | --url http://localhost:8090 | | --email | -e | Admin email | $POCKETBASE_EMAIL | --email [email protected] | | --password | -p | Admin password | $POCKETBASE_PASSWORD | --password admin123 | | --dir | -d | Output directory | - | --dir ./types | | --type | -t | Output type | ts | --type ts,zod | | --ignore | - | Ignore pattern (regex) | - | --ignore "^_.*" | | --include | - | Include pattern (regex) | - | --include "^(users\|posts).*" | | --filter | -f | Filter by collection | - | --filter users |

Type Options

  • ts - Generate TypeScript types only
  • zod - Generate Zod schemas only
  • ts,zod - Generate both TypeScript types and Zod schemas

Pattern Filtering

Control which collections get schemas generated using regex patterns:

[!WARNING] Collection Dependencies: When using --include or --ignore patterns, ensure that excluded collections are not referenced by included collections through relations. If Collection A has a relation field pointing to Collection B, and you exclude Collection B while including Collection A, the type generation will fail because it cannot resolve the relation target. Always include all collections that are referenced by the collections you want to generate.

# Only user-facing collections
npx typed-pocketbase --include "^(users|posts|comments).*" --type zod --dir ./types

# Exclude system collections (default behavior)
npx typed-pocketbase --ignore "^_.*" --type zod --dir ./types

# Include all collections
npx typed-pocketbase --ignore "" --type zod --dir ./types

# Combine patterns: include all but exclude secrets
npx typed-pocketbase --include ".*" --ignore "^_secrets$" --type zod --dir ./types

Zod Runtime Validation

Generated Schemas

For each collection, the tool generates:

  • Response Schema - {Collection}ResponseSchema for API responses
  • Create Schema - {Collection}CreateSchema for create operations
  • Update Schema - {Collection}UpdateSchema for update operations
  • Validation Helpers - {collection}Validators with safe parsing
  • TypeScript Types - Inferred from Zod schemas

Field Type Mappings

| PocketBase Type | Zod Schema | Notes | | --------------- | ---------------------------------------------------------------------- | -------------------------------- | | text | z.string() | With min/max/pattern validation | | email | z.email() | Email format validation (Zod v4) | | url | z.url() | URL format validation (Zod v4) | | number | z.number() | With min/max validation | | bool | z.boolean() | Boolean validation | | date | z.string() / z.union([z.string(), z.date()]) | Flexible date handling | | select | z.enum([...]) | Enum with allowed values | | relation | z.string() / z.array(z.string()) | Single or multiple relations | | file | z.instanceof(File) / z.string() | File handling | | json | z.union([z.record(z.string(), z.any()), z.array(z.any()), z.null()]) | Flexible JSON |

Validation Examples

import { z } from 'zod';
import { PostCreateSchema, postValidators } from './pb/pb-zod';

// Basic validation (throws on error)
const validData = PostCreateSchema.parse({
  title: 'My Post',
  content: 'Hello world',
  published: true
});

// Safe validation with error handling
const result = postValidators.safeCreate(formData);
if (result.success) {
  console.log('Valid data:', result.data);
  // Create record with validated data
  const record = await db.collection('posts').create(result.data);
} else {
  console.error('Validation errors:');
  result.error.issues.forEach(issue => {
    console.log(`${issue.path.join('.')}: ${issue.message}`);
  });
}

// Custom validation extensions
const CustomPostSchema = PostCreateSchema.extend({
  title: z.string().min(1, 'Title is required').max(100, 'Title too long')
}).refine(data => data.title !== 'forbidden', {
  message: 'This title is not allowed',
  path: ['title']
});

TypedPocketBase Client

Selecting Fields

const showId = Math.random() < 0.5;

db.from('posts').getFullList({
	select: {
		id: showId,
		title: true,
		content: true
	}
});

Filtering Columns

Use the and, or and other utility functions to filter rows:

import { and, or, eq, gte, lt } from '@tigawanna/typed-pocketbase';

// Get all posts created in 2022
db.from('posts').getFullList({
	filter: and(['date', '<', '2023-01-01'], ['date', '>=', '2022-01-01'])
});

// Combine filters with helpers
db.from('posts').getFullList({
	filter: or(
		['date', '>=', '2023-01-01'],
		lt('date', '2022-01-01')
	)
});

// Filter for columns in relations (up to 6 levels deep)
db.from('posts').getFullList({
	filter: eq('owner.name', 'me')
});

Sorting Rows

db.from('posts').getFullList({
	// Sort by descending 'date'
	sort: '-date'
});

db.from('posts').getFullList({
	// Sort by multiple fields
	sort: ['-date', '+title']
});

Expanding Relations

[!NOTE] Switched the indirect expand syntax from comments(posts)comments_via_post

db.from('posts').getFullList({
	select: {
		expand: {
			user: true,
			comments_via_post: true
		}
	}
});

// Select nested columns
db.from('posts').getFullList({
	select: {
		expand: {
			user: {
				name: true,
				avatar: true
			}
		}
	}
});

// Nested expand
db.from('posts').getFullList({
	select: {
		expand: {
			user: {
				expand: {
					profile: true
				}
			}
		}
	}
});

Batch APIs

const batch = db.fromBatch();
batch.from("users").create({...});
batch.from("users").upsert({...});
await batch.send();

Impersonate Client

const impersonateClient = await db.impersonate("_superusers", "user_id_being_impersonated", 20);
impersonateClient.from("posts").create({...});
impersonateClient.from("posts").update({...});

Helper Methods

createSelect

const select = db.from('posts').createSelect({
	id: true,
	content: true,
	owner: true,
	expand: {
		owner: {
			username: true,
			email: true
		}
	}
});

createFilter

const filter = db
	.from('posts')
	.createFilter(or(eq('content', 'bla'), eq('published', true)));

createSort

const sort = db.from('posts').createSort('+id', '-date');

Environment Variables

Set environment variables to avoid passing credentials in commands:

export [email protected]
export POCKETBASE_PASSWORD=admin123

# Now you can run without --email and --password
npx typed-pocketbase --url http://localhost:8090 --type zod --dir ./types

Best Practices

  1. Always validate external data - API responses, user input, etc.
  2. Use safe parsing for user-facing operations to handle errors gracefully
  3. Validate at boundaries - When data enters/exits your application
  4. Regenerate schemas when your PocketBase collections change
  5. Use pattern filtering for different environments (dev vs prod)

Programmatic API

import { generateTypes } from '@tigawanna/typed-pocketbase/codegen';

const result = await generateTypes({
  url: 'http://127.0.0.1:8090',
  email: '[email protected]',
  password: 'admin-password',
  ignorePattern: '^_.*',
  includePattern: '^(users|posts).*'
});

// Write generated files
await fs.writeFile('pb-types.ts', result.types);
await fs.writeFile('pb-zod.ts', result.zodSchemas);

License

MIT