To get the most out of pb-query, you should define TypeScript types that reflect your PocketBase collections. This allows pb-query to provide type safety and autocompletion based on your schema.
Create TypeScript interfaces for each of your PocketBase collections. Make sure to include all the fields you plan to use in your queries.
export interface User {
id: string;
name: string;
email: string;
created: Date;
updated: Date;
}
export interface Post {
id: string;
title: string;
content: string;
author: User; // relation
status: 'draft' | 'published';
created: Date;
updated: Date;
}
| PocketBase Field | TypeScript Type |
|---|---|
| Plain text, Rich editor, Email, URL | string |
| Number | number |
| Bool | boolean |
| Datetime, Autodate | Date |
| JSON | object, Array<T>, or custom type |
| Geo Point | Use the GeoPoint exposed by pb-query |
| Relation | Your collection interface ( e.g. User) |
| Multiple Fields | Array of the required type ( e.g. string[], User[] ) |
| Select | Union of string literals ( e.g. 'draft' | 'published') |
When you use a value of the wrong type in your query, TypeScript will raise an error, helping you catch mistakes early.
const query = pbQuery<Post>()
.between('created', new Date('2023-01-01'), new Date('2023-12-31')); // ✅ Correct
const query = pbQuery<Post>()
.equal('created', '@today'); // ✅ Correct
const query = pbQuery<Post>()
.equal('created', '2025'); // ❌ TypeScript Error
When building queries, make sure to specify the type of the collection you are querying.
const { filter, sort } = pbQuery<Post>() // posts collection
.search(['title', 'content', 'author.name'], 'footba')
.and()
.group((q) =>
q.equal('status', 'published')
.or()
.equal('author.name', 'Sergio')
)
.sort(['-created'])
.build(pb.filter);
const { filter, sort } = pbQuery<User>() // users collection
.like('name', 'Sergio')
.build(pb.filter);
If you're already using Pocketbase Typegen you can reuse the generated types with pb-query. However, they need some adjustments.
Pocketbase Typegen produces:
RecordIdString for relations.IsoDateString for date fields.These types are string-based, so pb-query can't infer relations or dates.
RecordIdString with the actual relation type. This is required, if you don't, you won't get type-checking for nested fields like author.name.IsoDateString with Date (optional, but recommended).import type { PostsRecord, UsersRecord } from './types-from-pocketbase-typegen'
type UsersQuery = Omit<UsersRecord, 'created' | 'updated'> & {
created: Date;
updated: Date;
};
type PostsQuery = Omit<PostsRecord, 'author' | 'created' | 'updated'> & {
author: UsersQuery;
created: Date;
updated: Date;
};
import type { PostsRecord, UsersRecord } from './types-from-pocketbase-typegen'
type UsersQuery = UsersRecord;
type PostsQuery = Omit<PostsRecord, 'author'> & {
author: UsersQuery;
};
*Record like PostsRecord. However, if you need to access additional fields like collectionId inside .fields(), you can use PostsResponse instead.Currently, pb-query doesn't have enough context to support back-relations properly. It would need to know a lot more about the collection schema, which complicates things quite a bit. We're working on proper back-relation support. Follow #12 for updates.
For now, you can use back-relation fields containing _via_, but they bypass type-checking:
const { filter } = pbQuery<User>()
.like('posts_via_author.name', 'Sergio') // ⚠️ No type-checked
.build(pb.filter);
posts_via_author.titel won't be caught by TypeScript.