$app variable is the global PocketBase application instance available in all JavaScript hooks. It provides access to the database, settings, mailer, and more.
Global access
The$app variable is automatically available in all .pb.js and .pb.ts files:
// Access app settings
const settings = $app.settings()
// Query the database
const record = $app.findFirstRecordByData("users", "email", "test@example.com")
// Send an email
const message = new MailerMessage({
from: {
address: settings.meta.senderAddress,
name: settings.meta.senderName,
},
to: [{address: "user@example.com"}],
subject: "Welcome!",
html: "<p>Thanks for signing up!</p>",
})
$app.newMailClient().send(message)
Database queries
Find records
// Find a single record by ID
const record = $app.findRecordById("posts", "RECORD_ID")
// Find by field value
const user = $app.findFirstRecordByData("users", "username", "john")
// Find all records by field value
const records = arrayOf(new Record())
$app.findAllRecordsByData("posts", "status", "published", records)
Query builder
For more complex queries, use the query builder:const records = arrayOf(new Record())
$app.recordQuery("articles")
.andWhere($dbx.hashExp({"status": "published"}))
.andWhere($dbx.like("title", "JavaScript"))
.orderBy("created DESC")
.limit(10)
.all(records)
console.log(records.length) // Number of records found
Database transactions
$app.runInTransaction((txApp) => {
// All operations here run in a transaction
const user = txApp.findRecordById("users", userId)
user.set("credits", user.getInt("credits") - 100)
txApp.save(user)
const purchase = new Record(txApp.findCollectionByNameOrId("purchases"))
purchase.set("user", userId)
purchase.set("amount", 100)
txApp.save(purchase)
// If any error occurs, all changes are rolled back
return null // Return error or null for success
})
Collections
Find collections
// By name or ID
const collection = $app.findCollectionByNameOrId("posts")
// Get all collections
const collections = arrayOf(new Collection())
$app.findAllCollections(collections)
Create collections
const collection = new Collection({
name: "articles",
type: "base",
listRule: "@request.auth.id != ''",
viewRule: "@request.auth.id != ''",
createRule: "@request.auth.id != ''",
updateRule: "@request.auth.id = @request.data.author",
deleteRule: "@request.auth.id = @request.data.author",
fields: [
{
name: "title",
type: "text",
required: true,
min: 3,
max: 200,
},
{
name: "content",
type: "editor",
required: true,
},
{
name: "author",
type: "relation",
required: true,
collectionId: $app.findCollectionByNameOrId("users").id,
},
{
name: "status",
type: "select",
required: true,
values: ["draft", "published", "archived"],
},
],
})
$app.save(collection)
Update collections
const collection = $app.findCollectionByNameOrId("posts")
// Modify collection properties
collection.listRule = "status = 'published' || @request.auth.id != ''"
// Add a new field
const fields = collection.fields
fields.push(new TextField({
name: "excerpt",
required: false,
max: 500,
}))
collection.fields = fields
$app.save(collection)
Authentication
Find auth records
// Find by email
const user = $app.findAuthRecordByEmail("users", "test@example.com")
// Find by token
const user = $app.findAuthRecordByToken("eyJhbGc...", "users")
Check permissions
const record = $app.findRecordById("posts", "RECORD_ID")
const info = new RequestInfo({
auth: $app.findAuthRecordByEmail("users", "john@example.com"),
data: {"title": "New Title"},
})
const canUpdate = $app.canAccessRecord(
record,
info,
"@request.auth.id = author"
)
if (!canUpdate) {
throw new ForbiddenError("You cannot update this post")
}
Settings
Read settings
const settings = $app.settings()
// App settings
const appName = settings.meta.appName
const appUrl = settings.meta.appUrl
// Email settings
const senderAddress = settings.meta.senderAddress
const senderName = settings.meta.senderName
// S3 storage
const s3Enabled = settings.s3.enabled
const s3Bucket = settings.s3.bucket
Update settings
const settings = $app.settings()
settings.meta.appName = "My Custom App"
settings.logs.maxDays = 14
$app.saveSettings(settings)
File storage
// Get the filesystem for a collection
const collection = $app.findCollectionByNameOrId("posts")
const fileSystem = $app.newFilesystem()
// Upload a file
const file = $filesystem.fileFromPath("/path/to/image.jpg")
fileSystem.upload(file, "posts/" + record.id + "/image.jpg")
// Delete a file
fileSystem.delete("posts/" + record.id + "/old-image.jpg")
Logging
// Info level
$app.logger().info("User logged in", "userId", user.id)
// Warning level
$app.logger().warn("High memory usage", "percent", 85)
// Error level
$app.logger().error("Failed to process payment", "error", err.message)
// Debug level (only shown when debug mode is enabled)
$app.logger().debug("Processing request", "path", e.request.url.path)
Utilities
Data directory
// Get the pb_data directory path
const dataDir = $app.dataDir()
// Read a custom file
const content = $os.readFile(dataDir + "/custom-config.json")
Refresh token
// Check if the app is in debug mode
const isDebug = $app.isDebug()
if (isDebug) {
console.log("Running in debug mode")
}
Subscriptions
// Send real-time message to subscribers
const message = new SubscriptionMessage({
name: "posts/RECORD_ID",
data: {"action": "update", "record": record},
})
$app.subscriptionsBroker().submit(message)
Examples
Custom email validation
onRecordCreateRequest((e) => {
const data = e.requestInfo().data
if (e.collection.name === "users") {
// Check if email domain is allowed
const email = data.email || ""
const allowedDomains = ["company.com", "partner.com"]
const domain = email.split("@")[1]
if (!allowedDomains.includes(domain)) {
throw new BadRequestError("Email domain not allowed")
}
}
return e.next()
}, "users")
Usage analytics
onRecordViewRequest((e) => {
// Track page views
const analytics = new Record(
$app.findCollectionByNameOrId("analytics")
)
analytics.set("record", e.record.id)
analytics.set("collection", e.collection.name)
analytics.set("user", e.requestInfo().auth?.id || "")
analytics.set("ip", e.realIP())
$app.save(analytics)
return e.next()
})
Custom admin command
const command = new Command({
use: "cleanup",
short: "Clean up old records",
run: (cmd, args) => {
const cutoff = new DateTime()
cutoff.addDate(0, 0, -30) // 30 days ago
const records = arrayOf(new Record())
$app.recordQuery("logs")
.andWhere($dbx.exp("created < {:cutoff}", {cutoff: cutoff.string()}))
.all(records)
for (let record of records) {
$app.delete(record)
}
console.log("Deleted", records.length, "old records")
},
})
$app.rootCmd().addCommand(command)