Skip to main content
The $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)