| name | PocketBase SDK |
| description | JavaScript SDK usage for PocketBase client applications. Use when calling PocketBase from frontend or Node.js, authenticating users, subscribing to realtime events, uploading files, or working with the PocketBase JS/TS SDK. Covers CRUD, auth flows, authStore, realtime SSE, file handling, batch operations, and query syntax. |
PocketBase JavaScript SDK
Installation & Setup
npm install pocketbase
yarn add pocketbase
<script src="https://cdn.jsdelivr.net/npm/pocketbase@0.36.6/dist/pocketbase.umd.js"></script>
import PocketBase from 'pocketbase'
const pb = new PocketBase('http://127.0.0.1:8090')
CRUD Operations
List records
const records = await pb.collection('posts').getList(1, 20, {
filter: 'status = "active" && created > "2024-01-01"',
sort: '-created,title',
expand: 'author,tags',
fields: 'id,title,author,created',
skipTotal: true,
})
Get full list (auto-paginate)
const allRecords = await pb.collection('posts').getFullList({
filter: 'status = "active"',
sort: '-created',
batch: 200,
})
View single record
const record = await pb.collection('posts').getOne('RECORD_ID', {
expand: 'author',
})
Get first matching record
const record = await pb.collection('posts').getFirstListItem('slug = "my-post"', {
expand: 'author',
})
Create record
const record = await pb.collection('posts').create({
title: 'My Post',
body: 'Content here',
author: 'USER_ID',
status: 'draft',
})
Update record
const record = await pb.collection('posts').update('RECORD_ID', {
title: 'Updated Title',
status: 'published',
})
Delete record
await pb.collection('posts').delete('RECORD_ID')
Query Parameters
Filter syntax
Same as API rules filter syntax. Common patterns:
filter: 'status = "active"'
filter: 'title ~ "hello"'
filter: 'tags ?= "TAG_ID"'
filter: 'created > "2024-01-01 00:00:00"'
filter: 'created > @now - 7d'
filter: 'status = "active" && author = "USER_ID"'
filter: '(type = "a" || type = "b") && active = true'
filter: 'parent = null'
filter: 'parent != null'
Sort syntax
sort: '-created'
sort: 'title'
sort: '-created,title'
sort: '@random'
Expand relations
expand: 'author'
expand: 'author,tags'
expand: 'author.team'
expand: 'comments_via_post'
expand: 'comments_via_post.author'
Fields (partial response)
fields: 'id,title,created'
fields: 'id,expand.author.name'
fields: '*,expand.author.name'
Authentication
Email/password
const authData = await pb.collection('users').authWithPassword('user@example.com', 'password123')
OAuth2 (all-in-one)
const authData = await pb.collection('users').authWithOAuth2({ provider: 'google' })
const authData = await pb.collection('users').authWithOAuth2({
provider: 'google',
urlCallback: (url) => { window.location.href = url }
})
OTP (one-time password)
const result = await pb.collection('users').requestOTP('user@example.com')
const authData = await pb.collection('users').authWithOTP(result.otpId, '123456')
MFA (multi-factor authentication)
MFA is triggered automatically when enabled. After primary auth returns a mfaId:
try {
await pb.collection('users').authWithPassword('user@example.com', 'password')
} catch (err) {
if (err.response?.mfaId) {
const otpResult = await pb.collection('users').requestOTP('user@example.com')
await pb.collection('users').authWithOTP(otpResult.otpId, '123456', {
mfaId: err.response.mfaId
})
}
}
Auth store
pb.authStore.token
pb.authStore.record
pb.authStore.isValid
pb.authStore.isAdmin
pb.authStore.isSuperuser
pb.authStore.onChange((token, record) => {
console.log('Auth changed:', record?.id)
})
pb.authStore.clear()
await pb.collection('users').authRefresh()
Password reset
await pb.collection('users').requestPasswordReset('user@example.com')
await pb.collection('users').confirmPasswordReset(token, newPassword, newPasswordConfirm)
Email verification
await pb.collection('users').requestVerification('user@example.com')
await pb.collection('users').confirmVerification(token)
Email change
await pb.collection('users').requestEmailChange('new@example.com')
await pb.collection('users').confirmEmailChange(token, password)
Realtime (SSE)
Subscribe to record changes
pb.collection('posts').subscribe('*', function(e) {
console.log(e.action, e.record.id)
}, {
expand: 'author',
filter: 'status = "active"',
})
pb.collection('posts').subscribe('RECORD_ID', function(e) {
console.log('Record changed:', e.record)
})
pb.collection('posts').unsubscribe('*')
pb.collection('posts').unsubscribe('RECORD_ID')
pb.collection('posts').unsubscribe()
pb.realtime.unsubscribe()
Connection management
pb.realtime.onConnect = function() {
console.log('Connected')
}
pb.realtime.onDisconnect = function() {
console.log('Disconnected')
}
File Upload & Download
Upload files
const formData = new FormData()
formData.append('title', 'My Post')
formData.append('document', fileInput.files[0])
formData.append('images', fileInput1.files[0])
formData.append('images', fileInput2.files[0])
const record = await pb.collection('posts').create(formData)
const record = await pb.collection('posts').create({
title: 'My Post',
document: new File([blob], 'file.pdf'),
})
Delete a file
await pb.collection('posts').update('RECORD_ID', {
document: null,
})
await pb.collection('posts').update('RECORD_ID', {
'images-': ['filename_to_remove.jpg'],
})
Get file URL
const url = pb.files.getURL(record, record.document)
const thumb = pb.files.getURL(record, record.cover, { thumb: '100x100' })
Protected files
For files in collections with view rules, include the auth token:
const url = pb.files.getURL(record, record.document, { token: pb.authStore.token })
Batch Operations
Send multiple create/update/delete in one request (transactional):
const batch = pb.createBatch()
batch.collection('posts').create({ title: 'Post 1' })
batch.collection('posts').create({ title: 'Post 2' })
batch.collection('posts').update('RECORD_ID', { title: 'Updated' })
batch.collection('comments').delete('COMMENT_ID')
const results = await batch.send()
Error Handling
try {
const record = await pb.collection('posts').create(data)
} catch (err) {
if (err.status === 400) {
for (const [field, error] of Object.entries(err.response.data)) {
console.log(`${field}: ${error.message}`)
}
}
}
Advanced
Auto-cancellation
By default, duplicate pending requests to the same endpoint are auto-cancelled. Disable per-request:
await pb.collection('posts').getList(1, 20, {
requestKey: null,
})
await pb.collection('posts').getList(1, 20, {
requestKey: 'my-custom-key',
})
Custom headers
await pb.collection('posts').getList(1, 20, {
headers: { 'X-Custom': 'value' }
})
pb.beforeSend = function(url, options) {
options.headers['X-Custom'] = 'value'
return { url, options }
}
pb.afterSend = function(response, data) {
return data
}
SSR / Server-side
pb.authStore.loadFromCookie(request.headers.get('cookie') || '')
const cookie = pb.authStore.exportToCookie({ httpOnly: false })
response.headers.set('set-cookie', cookie)
Sending as superuser
const pb = new PocketBase('http://127.0.0.1:8090')
await pb.collection('_superusers').authWithPassword('admin@example.com', 'password')