Official JavaScript SDK (browser and node) for interacting with the PocketBase API.
<script src="https://github.com/pocketbase/js-sdk/raw/v0.27.0/path/to/dist/pocketbase.umd.js"></script>
<script type="text/javascript">
const pb = new PocketBase("https://example.com")
...
</script>
OR if you are using ES modules:
<script type="module">
import PocketBase from '/path/to/dist/pocketbase.es.mjs'
const pb = new PocketBase("https://example.com")
...
</script>
npm install pocketbase --save
// Using ES modules (default)
import PocketBase from 'pocketbase'
// OR if you are using CommonJS modules
const PocketBase = require('pocketbase/cjs')
🔧 For Node < 17 you'll need to load a
fetch()polyfill. I recommend lquixada/cross-fetch:js // npm install cross-fetch --save import 'cross-fetch/polyfill';
🔧 Node doesn't have native
EventSourceimplementation, so in order to use the realtime subscriptions you'll need to load aEventSourcepolyfill. ```js // for server: npm install eventsource --save import { EventSource } from "eventsource";// for React Native: npm install react-native-sse --save import EventSource from "react-native-sse";
global.EventSource = EventSource; ```
import PocketBase from 'pocketbase';
const pb = new PocketBase('http://127.0.0.1:8090');
...
// authenticate as auth collection record
const userData = await pb.collection('users').authWithPassword('test@example.com', '123456');
// list and filter "example" collection records
const result = await pb.collection('example').getList(1, 20, {
filter: 'status = true && created > "2022-08-01 10:00:00"'
});
// and much more...
More detailed API docs and copy-paste examples could be found in the API documentation for each service.
The SDK comes with a helper pb.filter(expr, params) method to generate a filter string with placeholder parameters ({:paramName}) populated from an object.
This method is also recommended when using the SDK in Node/Deno/Bun server-side list queries and accepting untrusted user input as filter string arguments, because it will take care to properly escape the generated string expression, avoiding eventual string injection attacks (on the client-side this is not much of an issue).
const records = await pb.collection("example").getList(1, 20, {
// the same as: "title ~ 'te\\'st' && (totalA = 123 || totalB = 123)"
filter: pb.filter("title ~ {:title} && (totalA = {:num} || totalB = {:num})", { title: "te'st", num: 123 })
})
The supported placeholder parameter values are:
string (single quotes are autoescaped)numberbooleanDate object (will be stringified into the format expected by PocketBase)nullJSON.stringify()PocketBase Web API supports file upload via multipart/form-data requests,
which means that to upload a file it is enough to provide either a FormData instance OR plain object with File/Blob prop values.
Using plain object as body (this is the same as above and it will be converted to FormData behind the scenes):
```js
const data = {
'title': 'lorem ipsum...',
'document': new File(...),
};
await pb.collection('example').create(data); ```
Using FormData as body:
```js
// the standard way to create multipart/form-data body
const data = new FormData();
data.set('title', 'lorem ipsum...')
data.set('document', new File(...))
await pb.collection('example').create(data); ```
All services return a standard Promise-based response, so the error handling is straightforward:
pb.collection('example').getList(1, 50).then((result) => {
// success...
console.log('Result:', result);
}).catch((error) => {
// error...
console.log('Error:', error);
});
// OR if you are using the async/await syntax:
try {
const result = await pb.collection('example').getList(1, 50);
console.log('Result:', result);
} catch (error) {
console.log('Error:', error);
}
The response error is normalized and always returned as ClientResponseError object with the following public fields that you could use:
ClientResponseError {
url: string, // requested url
status: number, // response status code
response: { ... }, // the API JSON error response
isAbort: boolean, // is abort/cancellation error
originalError: Error|null, // the original non-normalized error
}
The SDK keeps track of the authenticated token and auth model for you via the pb.authStore instance.
The default LocalAuthStore uses the browser's LocalStorage if available, otherwise - will fallback to runtime/memory (aka. on page refresh or service restart you'll have to authenticate again).
Conveniently, the default store also takes care to automatically sync the auth store state between multiple tabs.
NB! Deno also supports
LocalStoragebut keep in mind that, unlike in browsers where the client is the only user, by default DenoLocalStoragewill be shared by all clients making requests to your server!
The SDK comes also with a helper AsyncAuthStore that you can use to integrate with any 3rd party async storage implementation (usually this is needed when working with React Native):
import AsyncStorage from '@react-native-async-storage/async-storage';
import PocketBase, { AsyncAuthStore } from 'pocketbase';
const store = new AsyncAuthStore({
save: async (serialized) => AsyncStorage.setItem('pb_auth', serialized),
initial: AsyncStorage.getItem('pb_auth'),
});
const pb = new PocketBase('http://127.0.0.1:8090', store)
In some situations it could be easier to create your own custom auth store. For this you can extend BaseAuthStore and pass the new custom instance as constructor argument to the client:
import PocketBase, { BaseAuthStore } from 'pocketbase';
class CustomAuthStore extends BaseAuthStore {
save(token, model) {
super.save(token, model);
// your custom business logic...
}
}
const pb = new PocketBase('http://127.0.0.1:8090', new CustomAuthStore());
The default pb.authStore extends BaseAuthStore and has the following public members that you can use:
BaseAuthStore {
// base fields
record: RecordModel|null // the authenticated auth record
token: string // the authenticated token
isValid: boolean // checks if the store has existing and unexpired token
isSuperuser: boolean // checks if the store state is for superuser
// main methods
clear() // "logout" the authenticated record
save(token, record) // update the store with the new auth data
onChange(callback, fireImmediately = false) // register a callback that will be called on store change
// cookie parse and serialize helpers
loadFromCookie(cookieHeader, key = 'pb_auth')
exportToCookie(options = {}, key = 'pb_auth')
}
To "logout" the authenticated record you can call pb.authStore.clear().
To "listen" for changes in the auth store, you can register a new listener via pb.authStore.onChange, eg:
// triggered everytime on store change
const removeListener1 = pb.authStore.onChange((token, record) => {
console.log('New store data 1:', token, record)
});
// triggered once right after registration and everytime on store change
const removeListener2 = pb.authStore.onChange((token, record) => {
console.log('New store data 2:', token, record)
}, true);
// (optional) removes the attached listeners
removeListener1();
removeListener2();
The SDK client will auto cancel duplicated pending requests for you.
For example, if you have the following 3 duplicated endpoint calls, only the last one will be executed, while the first 2 will be cancelled with ClientResponseError error:
pb.collection('example').getList(1, 20) // cancelled
pb.collection('example').getList(2, 20) // cancelled
pb.collection('example').getList(3, 20) // executed
To change this behavior per request basis, you can adjust the requestKey: null|string special query parameter.
Set it to null to unset the default request identifier and to disable auto cancellation for the specific request.
Or set it to a unique string that will be used as request identifier and based on which pending requests will be matched (default to HTTP_METHOD + path, eg. "GET /api/users")
If you want to globally disable the auto cancellation behavior, you could set pb.autoCancellation(false).
Examples:
pb.collection('example').getList(1, 20); // cancelled
pb.collection('example').getList(1, 20); // executed
pb.collection('example').getList(1, 20, { requestKey: "test" }) // cancelled
pb.collection('example').getList(1, 20, { requestKey: "test" }) // executed
pb.collection('example').getList(1, 20, { requestKey: null }) // executed
pb.collection('example').getList(1, 20, { requestKey: null }) // executed
// globally disable auto cancellation
pb.autoCancellation(false);
pb.collection('example').getList(1, 20); // executed
pb.collection('example').getList(1, 20); // executed
pb.collection('example').getList(1, 20); // executed
If you want to manually cancel pending requests, you could use pb.cancelAllRequests() or pb.cancelRequest(requestKey).
You could specify custom TypeScript definitions for your Record models using generics:
interface Task {
// type the collection fields you want to use...
id: string;
name: string;
}
pb.collection('tasks').getList<Task>(1, 20) // -> results in Promise<ListResult<Task>>
pb.collection('tasks').getOne<Task>("RECORD_ID") // -> results in Promise<Task>
Alternatively, if you don't want to type the generic argument every time you can define a global PocketBase type using type assertion:
interface Task {
id: string;
name: string;
}
interface Post {
id: string;
title: string;
active: boolean;
}
interface TypedPocketBase extends PocketBase {
collection(idOrName: string): RecordService // default fallback for any other collection
collection(idOrName: 'tasks'): RecordService<Task>
collection(idOrName: 'posts'): RecordService<Post>
}
...
const pb = new PocketBase("http://127.0.0.1:8090") as TypedPocketBase;
pb.collection('tasks').getOne("RECORD_ID") // -> results in Promise<Task>
pb.collection('posts').getOne("RECORD_ID") // -> results in Promise<Post>
All API services accept an optional options argument (usually the last one and of type SendOptions), that can be used to provide:
fetch implementationFor example:
pb.collection('example').getList(1, 20, {
expand: 'someRel',
otherQueryParam: '123',
// custom headers
headers: {
'X-Custom-Header': 'example',
},
// custom fetch options
keepalive: false,
cache: 'no-store',
// or custom fetch implementation
fetch: async (url, config) => { ... },
})
Note that for backward compatability and to minimize the verbosity, any "unknown" top-level field will be treated as query parameter.
Sometimes you may want to modify the request data globally or to customize the response.
To accomplish this, the SDK provides 2 function hooks:
beforeSend - triggered right before sending the fetch request, allowing you to inspect/modify the request config.
```js
const pb = new PocketBase('http://127.0.0.1:8090');
pb.beforeSend = function (url, options) { // For list of the possible request options properties check // https://developer.mozilla.org/en-US/docs/Web/API/fetch#options options.headers = Object.assign({}, options.headers, { 'X-Custom-Header': 'example',
$ claude mcp add js-sdk \
-- python -m otcore.mcp_server <graph>