Skip to main content
Collections are the foundation of your PocketBase database. They define the structure of your data with fields, validation rules, and access controls.

Collection types

PocketBase supports three types of collections:
  • Base - Standard collections for general data storage
  • Auth - Collections with built-in authentication (email, password, OAuth2)
  • View - Read-only collections based on SQL queries

Creating collections

Base collection

const collection = new Collection({
  name: "posts",
  type: "base",
  listRule: "@request.auth.id != ''",
  viewRule: "@request.auth.id != ''",
  createRule: "@request.auth.id != ''",
  updateRule: "@request.auth.id = author",
  deleteRule: "@request.auth.id = 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,
      maxSelect: 1,
    },
  ],
})

$app.save(collection)

Auth collection

const collection = new Collection({
  name: "customers",
  type: "auth",
  authRule: "@request.auth.id != '' && verified = true",
  manageRule: "@request.auth.id != '' && @request.auth.role = 'admin'",
  fields: [
    {
      name: "company",
      type: "text",
      required: true,
    },
    {
      name: "role",
      type: "select",
      required: true,
      values: ["user", "admin", "moderator"],
    },
    {
      name: "avatar",
      type: "file",
      maxSelect: 1,
      maxSize: 5242880, // 5MB
      mimeTypes: ["image/jpeg", "image/png"],
    },
  ],
})

$app.save(collection)

View collection

const collection = new Collection({
  name: "posts_with_authors",
  type: "view",
  viewQuery: `
    SELECT
      posts.id,
      posts.title,
      posts.created,
      users.username as author_name,
      users.email as author_email
    FROM posts
    LEFT JOIN users ON posts.author = users.id
  `,
  listRule: "@request.auth.id != ''",
  viewRule: "@request.auth.id != ''",
})

$app.save(collection)

Field types

PocketBase supports multiple field types. Here are examples of each:

Text field

new TextField({
  name: "title",
  required: true,
  min: 3,
  max: 200,
  pattern: "^[a-zA-Z0-9\\s]+$", // Alphanumeric with spaces
})

Number field

new NumberField({
  name: "price",
  required: true,
  min: 0,
  max: 999999,
  onlyInt: false, // Allow decimals
})

Boolean field

new BoolField({
  name: "featured",
  required: false,
})

Email field

new EmailField({
  name: "contact_email",
  required: true,
  exceptDomains: ["tempmail.com", "throwaway.email"],
})

URL field

new URLField({
  name: "website",
  required: false,
  onlyDomains: ["example.com", "trusted-site.com"],
})

Date field

new DateField({
  name: "publish_date",
  required: true,
  min: "2024-01-01 00:00:00.000Z",
  max: "2025-12-31 23:59:59.999Z",
})

Select field

new SelectField({
  name: "status",
  required: true,
  values: ["draft", "published", "archived"],
  maxSelect: 1, // Single select
})

// Multi-select
new SelectField({
  name: "tags",
  required: false,
  values: ["javascript", "typescript", "go", "python"],
  maxSelect: 5, // Allow multiple selections
})

JSON field

new JSONField({
  name: "metadata",
  required: false,
  maxSize: 2000000, // 2MB max
})

File field

new FileField({
  name: "attachments",
  required: false,
  maxSelect: 5, // Up to 5 files
  maxSize: 10485760, // 10MB per file
  mimeTypes: [
    "image/jpeg",
    "image/png",
    "application/pdf",
    "application/zip",
  ],
  thumbs: ["100x100", "500x500"], // Image thumbnails
  protected: false, // Public access
})

Relation field

new RelationField({
  name: "author",
  required: true,
  collectionId: $app.findCollectionByNameOrId("users").id,
  maxSelect: 1, // Single relation
  cascadeDelete: false, // Don't delete related records
})

// Many-to-many relation
new RelationField({
  name: "categories",
  required: false,
  collectionId: $app.findCollectionByNameOrId("categories").id,
  maxSelect: 10, // Multiple relations
  cascadeDelete: false,
})

Editor field

new EditorField({
  name: "content",
  required: true,
  maxSize: 5000000, // 5MB
  convertUrls: true, // Convert file URLs to relative paths
})

Autodate field

new AutodateField({
  name: "updated_at",
  onCreate: false, // Don't set on create
  onUpdate: true, // Update on every save
})

new AutodateField({
  name: "created_at",
  onCreate: true, // Set once on create
  onUpdate: false, // Don't change on updates
})

Access rules

Access rules use a filter syntax to control who can perform operations on collections.

Common patterns

// Public read, authenticated write
const collection = new Collection({
  name: "articles",
  type: "base",
  listRule: "", // Anyone can list
  viewRule: "", // Anyone can view
  createRule: "@request.auth.id != ''", // Must be authenticated
  updateRule: "@request.auth.id = author", // Must be the author
  deleteRule: "@request.auth.id = author",
})

Role-based access

const collection = new Collection({
  name: "admin_posts",
  type: "base",
  listRule: "@request.auth.role = 'admin'",
  viewRule: "@request.auth.role = 'admin'",
  createRule: "@request.auth.role = 'admin'",
  updateRule: "@request.auth.role = 'admin'",
  deleteRule: "@request.auth.role = 'admin'",
})

Field-level access

const collection = new Collection({
  name: "posts",
  type: "base",
  // Only show published posts to non-authors
  listRule: "status = 'published' || @request.auth.id = author",
  viewRule: "status = 'published' || @request.auth.id = author",
  // Can create if authenticated
  createRule: "@request.auth.id != ''",
  // Can only update own posts and only certain fields
  updateRule: "@request.auth.id = author && @request.data.status:isset = false",
})

Finding collections

// By name
const collection = $app.findCollectionByNameOrId("posts")

// By ID
const collection = $app.findCollectionByNameOrId("abc123def456")

// Get all collections
const collections = arrayOf(new Collection())
$app.findAllCollections(collections)

for (let col of collections) {
  console.log(col.name, col.type)
}

Updating collections

Modify existing fields

const collection = $app.findCollectionByNameOrId("posts")

// Update a field
const fields = collection.fields
for (let i = 0; i < fields.length; i++) {
  if (fields[i].name === "title") {
    fields[i].max = 300 // Increase max length
  }
}
collection.fields = fields

$app.save(collection)

Add new fields

const collection = $app.findCollectionByNameOrId("posts")

const fields = collection.fields
fields.push(new TextField({
  name: "excerpt",
  required: false,
  max: 500,
}))

fields.push(new NumberField({
  name: "view_count",
  required: false,
  min: 0,
  onlyInt: true,
}))

collection.fields = fields
$app.save(collection)

Update rules

const collection = $app.findCollectionByNameOrId("posts")

// Make collection public for reading
collection.listRule = ""
collection.viewRule = ""

// But still require auth for modifications
collection.createRule = "@request.auth.id != ''"
collection.updateRule = "@request.auth.id = author"
collection.deleteRule = "@request.auth.id = author"

$app.save(collection)

Deleting collections

Deleting a collection will permanently delete all records in that collection. This action cannot be undone.
const collection = $app.findCollectionByNameOrId("old_posts")
$app.delete(collection)

Migration example

Here’s a complete example of creating a blog schema:
// pb_migrations/1234567890_create_blog.js

migrate(
  // Up migration
  (app) => {
    // Categories collection
    const categories = new Collection({
      name: "categories",
      type: "base",
      listRule: "",
      viewRule: "",
      createRule: "@request.auth.role = 'admin'",
      updateRule: "@request.auth.role = 'admin'",
      deleteRule: "@request.auth.role = 'admin'",
      fields: [
        {
          name: "name",
          type: "text",
          required: true,
          min: 2,
          max: 100,
        },
        {
          name: "slug",
          type: "text",
          required: true,
          pattern: "^[a-z0-9-]+$",
        },
      ],
    })
    app.save(categories)

    // Posts collection
    const posts = new Collection({
      name: "posts",
      type: "base",
      listRule: "status = 'published' || @request.auth.id = author",
      viewRule: "status = 'published' || @request.auth.id = author",
      createRule: "@request.auth.id != ''",
      updateRule: "@request.auth.id = author",
      deleteRule: "@request.auth.id = author",
      fields: [
        {
          name: "title",
          type: "text",
          required: true,
          min: 3,
          max: 200,
        },
        {
          name: "slug",
          type: "text",
          required: true,
          pattern: "^[a-z0-9-]+$",
        },
        {
          name: "content",
          type: "editor",
          required: true,
        },
        {
          name: "excerpt",
          type: "text",
          max: 500,
        },
        {
          name: "featured_image",
          type: "file",
          maxSelect: 1,
          maxSize: 5242880,
          mimeTypes: ["image/jpeg", "image/png", "image/webp"],
          thumbs: ["200x200", "800x600"],
        },
        {
          name: "author",
          type: "relation",
          required: true,
          collectionId: app.findCollectionByNameOrId("users").id,
          maxSelect: 1,
        },
        {
          name: "categories",
          type: "relation",
          collectionId: categories.id,
          maxSelect: 5,
        },
        {
          name: "status",
          type: "select",
          required: true,
          values: ["draft", "published", "archived"],
        },
        {
          name: "published_at",
          type: "date",
        },
      ],
    })
    app.save(posts)

    return null
  },
  // Down migration
  (app) => {
    app.delete(app.findCollectionByNameOrId("posts"))
    app.delete(app.findCollectionByNameOrId("categories"))
    return null
  }
)