PocketBase is an open-source backend that combines a SQLite database, auth system, realtime subscriptions, and file storage in a single executable. This comprehensive guide covers everything you need to know.
What is PocketBase?
PocketBase is a backend solution written in Go that provides everything you need to build modern web and mobile applications.
# Download and run
./pocketbase serve
Features include:
- SQLite Database - Embedded, portable database
- User Authentication - Email, OAuth, and anonymous auth
-
- Realtime Subscriptions - Server-Sent Events
- File Storage - Local and S3-compatible
-
- Admin Dashboard - Built-in UI
-
- REST API - Full CRUD operations
Installation and Setup
Running PocketBase
# Download
curl -L -o pocketbase https://github.com/pocketbase/pocketbase/releases/latest/download/pocketbase_*.zip
# Extract
unzip pocketbase_*.zip
# Run
./pocketbase serve
# Or run in background
./pocketbase serve --http=127.0.0.1:8090
First Run
# Visit admin UI
# http://127.0.0.1:8090/_/
# Create your first admin account
# Email: [email protected]
# Password: your-secure-password
Database Schema
Collections
Create collections via the Admin UI or API:
# Create collection via API (as admin)
curl -X POST http://127.0.0.1:8090/api/collections \
-H "Authorization: YOUR_ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "articles",
"type": "base",
"system": false,
"schema": [
{
"system": "id",
"name": "title",
"type": "text",
"required": true
},
{
"name": "content",
"type": "text",
"required": true
},
{
"name": "slug",
"type": "text",
"required": true,
"options": {
"pattern": "^[a-z0-9-]+$"
}
},
{
"name": "published",
"type": "bool",
"required": false
},
{
"name": "author",
"type": "relation",
"required": true,
"options": {
"collectionId": "_users",
"cascadeDelete": false,
"maxSelect": 1
}
}
],
"listRule": "published = true",
"viewRule": "published = true",
"createRule": "@request.auth.id != ''",
"updateRule": "@request.auth.id = author.id",
"deleteRule": "@request.auth.id = author.id"
}'
Field Types
PocketBase supports various field types:
- Text - Single line, multi-line
- Editor - Rich text (Markdown)
- Number - Integer, Float
- Bool - Boolean
- Email - Validated email
- URL - Validated URL
- File - Single/Multiple files
- Relation - One-to-one, one-to-many
- JSON - JSON object/array
- Date - Date and datetime
- Select - Single/Multi select
- User - Relation to users collection
Authentication
User Registration
// Register new user
const result = await pb.collection('users').create({
email: '[email protected]',
password: 'secure-password',
passwordConfirm: 'secure-password',
name: 'John Doe'
});
console.log(result);
User Login
// Email/Password login
const authData = await pb.collection('users').authWithPassword(
'[email protected]',
'secure-password'
);
console.log(authData);
console.log(pb.authStore.isValid);
console.log(pb.authStore.token);
OAuth2 Login
// Google OAuth
await pb.collection('users').authWithOAuth2({
provider: 'google'
});
// With redirect
await pb.collection('users').authWithOAuth2({
provider: 'google',
createData: {
name: profile.name,
avatarUrl: profile.avatarUrl
}
});
Auth Store
// Check authentication state
pb.authStore.isValid; // true/false
// Get current user
pb.authStore.model; // User object
// Refresh token
await pb.collection('users').authRefresh();
// Logout
pb.authStore.clear();
JavaScript SDK
Installation
# Via npm
npm install pocketbase
# Via CDN
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/pocketbase.umd.js"></script>
Basic Usage
import PocketBase from 'pocketbase';
const pb = new PocketBase('http://127.0.0.1:8090');
// Create record
const record = await pb.collection('articles').create({
title: 'My First Article',
content: 'Content goes here...',
slug: 'my-first-article',
published: true,
author: pb.authStore.model.id
});
// Get records
const records = await pb.collection('articles').getList(1, 20, {
sort: '-created',
filter: 'published = true'
});
// Get single record
const article = await pb.collection('articles').getOne('RECORD_ID');
// Update record
await pb.collection('articles').update('RECORD_ID', {
title: 'Updated Title'
});
// Delete record
await pb.collection('articles').delete('RECORD_ID');
Querying
// Filter
const records = await pb.collection('articles').getList(1, 10, {
filter: 'published = true && created >= "2024-01-01"',
sort: '-created,title'
});
// Full-text search
const search = await pb.collection('articles').getList(1, 10, {
filter: 'title ?~ "javascript"'
});
// Relations
const records = await pb.collection('articles').getList(1, 10, {
expand: 'author',
filter: 'author.name ?~ "John"'
});
// Select specific fields
const records = await pb.collection('articles').getList(1, 10, {
fields: 'id,title,slug,author.id,author.name'
});
Realtime Subscriptions
// Subscribe to changes
pb.collection('articles').subscribe('*', function (e) {
console.log(e.action); // "create" | "update" | "delete"
console.log(e.record); // The changed record
});
// Subscribe to specific record
pb.collection('articles').subscribe('RECORD_ID', function (e) {
console.log(e.action);
console.log(e.record);
});
// Unsubscribe
pb.collection('articles').unsubscribe();
// Unsubscribe all
pb.collection('articles').unsubscribe('*');
File Handling
Uploading Files
// Create with file
const formData = new FormData();
formData.append('title', 'Article with Image');
formData.append('image', fileInput.files[0]);
const record = await pb.collection('articles').create(formData);
// Update with file
await pb.collection('articles').update('RECORD_ID', {
'image': fileInput.files[0]
});
Serving Files
// Get file URL
const url = pb.files.getUrl(record, record.image, {
thumb: '100x100' // Generate thumbnail
});
// Different thumbnail presets
// '100x100' - fit
// '100x100!' - crop
// '100x' - width only
// 'x100' - height only
S3 Storage
Configure in pb_config.yaml:
filesystem:
endpoint: https://s3.amazonaws.com
bucket: my-bucket
region: us-east-1
accessKey: your-access-key
secretKey: your-secret-key
forcePathStyle: false
API Rules
Collection Rules
// Everyone can view
listRule: ""
viewRule: ""
// Authenticated users only
listRule: "@request.auth.id != ''"
// Owner only
listRule: "@request.auth.id = user.id"
// Field-level
{
"name": "email",
"system": false,
"type": "email",
"required": true,
"presentable": false,
"views": {},
"createRule": null,
"updateRule": "@request.auth.id = user.id",
"deleteRule": null
}
Available Variables
@request.id- Request unique ID@request.auth.id- Authenticated user ID@request.auth.email- Authenticated user email@collection.field- Field value from current record@collection.expand.field- Expanded relation field
Admin API
Authentication
// As admin (using superuser API token)
pb.admins.authWithToken('ADMIN_TOKEN');
// Or with email/password
await pb.admins.authWithPassword('[email protected]', 'password');
Managing Collections
// List collections
const collections = await pb.collections.getList(1, 50);
// Create collection
await pb.collections.create({
name: 'products',
type: 'base',
schema: [
{ name: 'name', type: 'text', required: true },
{ name: 'price', type: 'number', required: true }
]
});
// Delete collection
await pb.collections.delete('COLLECTION_ID');
Building a Full-Stack App
Frontend Example
<!DOCTYPE html>
<html>
<head>
<title>PocketBase Todo</title>
<script src="https://unpkg.com/[email protected]/dist/pocketbase.umd.js"></script>
</head>
<body>
<h1>Todo App</h1>
<div id="auth-section">
<input type="email" id="email" placeholder="Email">
<input type="password" id="password" placeholder="Password">
<button onclick="login()">Login</button>
<button onclick="register()">Register</button>
</div>
<div id="app-section" style="display:none">
<input type="text" id="todo-input" placeholder="New todo">
<button onclick="addTodo()">Add</button>
<ul id="todo-list"></ul>
<button onclick="logout()">Logout</button>
</div>
<script>
const pb = new PocketBase('http://127.0.0.1:8090');
async function register() {
try {
await pb.collection('users').create({
email: document.getElementById('email').value,
password: document.getElementById('password').value,
passwordConfirm: document.getElementById('password').value
});
alert('Registered! Please login.');
} catch (e) {
alert(e.message);
}
}
async function login() {
try {
await pb.collection('users').authWithPassword(
document.getElementById('email').value,
document.getElementById('password').value
);
showApp();
} catch (e) {
alert(e.message);
}
}
async function addTodo() {
const input = document.getElementById('todo-input');
await pb.collection('todos').create({
title: input.value,
user: pb.authStore.model.id
});
input.value = '';
loadTodos();
}
async function loadTodos() {
const todos = await pb.collection('todos').getList(1, 50, {
filter: `user = "${pb.authStore.model.id}"`,
sort: '-created'
});
const list = document.getElementById('todo-list');
list.innerHTML = todos.items.map(t =>
`<li>${t.title} <button onclick="deleteTodo('${t.id}')">X</button></li>`
).join('');
}
async function deleteTodo(id) {
await pb.collection('todos').delete(id);
loadTodos();
}
function showApp() {
document.getElementById('auth-section').style.display = 'none';
document.getElementById('app-section').style.display = 'block';
loadTodos();
}
function logout() {
pb.authStore.clear();
location.reload();
}
// Check auth
if (pb.authStore.isValid) {
showApp();
}
</script>
</body>
</html>
External Resources
- PocketBase Official Documentation
- PocketBase GitHub Repository
- PocketBase JavaScript SDK
- PocketBase Community
Conclusion
PocketBase provides an excellent backend solution for small to medium applications. Key points:
- Single executable - no complex setup
- SQLite database with relational support
- Built-in authentication with OAuth
- Realtime subscriptions via SSE
- File storage with S3 support
- Admin UI for data management
For rapid prototyping and smaller applications, PocketBase offers excellent value with minimal infrastructure requirements.
Comments