Files
website-enchun-mgr/.agent/skills/payload-cms/references/queries.md
pkupuk ad8e2e313e chore(agent): configure AI agents and tools
Add configuration for BMad, Claude, OpenCode, and other AI agent tools and workflows.
2026-02-11 11:51:23 +08:00

5.7 KiB

Queries Reference

Local API

Find Multiple

const result = await payload.find({
  collection: 'posts',
  where: {
    status: { equals: 'published' },
  },
  limit: 10,
  page: 1,
  sort: '-createdAt',
  depth: 2,
})

// Result structure
{
  docs: Post[],
  totalDocs: number,
  limit: number,
  totalPages: number,
  page: number,
  pagingCounter: number,
  hasPrevPage: boolean,
  hasNextPage: boolean,
  prevPage: number | null,
  nextPage: number | null,
}

Find By ID

const post = await payload.findByID({
  collection: 'posts',
  id: '123',
  depth: 2,
})

Create

const newPost = await payload.create({
  collection: 'posts',
  data: {
    title: 'New Post',
    content: '...',
    author: userId,
  },
  user: req.user, // For access control
})

Update

const updated = await payload.update({
  collection: 'posts',
  id: '123',
  data: {
    title: 'Updated Title',
  },
})

Delete

const deleted = await payload.delete({
  collection: 'posts',
  id: '123',
})

Query Operators

Comparison

where: {
  price: { equals: 100 },
  price: { not_equals: 100 },
  price: { greater_than: 100 },
  price: { greater_than_equal: 100 },
  price: { less_than: 100 },
  price: { less_than_equal: 100 },
}

String Operations

where: {
  title: { like: 'Hello' },        // Case-insensitive contains
  title: { contains: 'world' },    // Case-sensitive contains
  email: { exists: true },         // Field has value
}

Array Operations

where: {
  tags: { in: ['tech', 'design'] },      // Value in array
  tags: { not_in: ['spam'] },            // Value not in array
  tags: { all: ['featured', 'popular'] }, // Has all values
}

AND/OR Logic

where: {
  and: [
    { status: { equals: 'published' } },
    { author: { equals: userId } },
  ],
}

where: {
  or: [
    { status: { equals: 'published' } },
    { author: { equals: userId } },
  ],
}

// Nested
where: {
  and: [
    { status: { equals: 'published' } },
    {
      or: [
        { featured: { equals: true } },
        { 'author.roles': { in: ['admin'] } },
      ],
    },
  ],
}

Nested Properties

Query through relationships:

where: {
  'author.name': { contains: 'John' },
  'category.slug': { equals: 'tech' },
}

Geospatial Queries

where: {
  location: {
    near: [-73.935242, 40.730610, 10000], // [lng, lat, maxDistanceMeters]
  },
}

where: {
  location: {
    within: {
      type: 'Polygon',
      coordinates: [[[-74, 40], [-73, 40], [-73, 41], [-74, 41], [-74, 40]]],
    },
  },
}

Field Selection

Only fetch specific fields:

const posts = await payload.find({
  collection: 'posts',
  select: {
    title: true,
    slug: true,
    author: true, // Will be populated based on depth
  },
})

Depth (Relationship Population)

// depth: 0 - IDs only
{ author: '123' }

// depth: 1 - First level populated
{ author: { id: '123', name: 'John' } }

// depth: 2 (default) - Nested relationships populated
{ author: { id: '123', name: 'John', avatar: { url: '...' } } }

Pagination

// Page-based
await payload.find({
  collection: 'posts',
  page: 2,
  limit: 20,
})

// Cursor-based (more efficient for large datasets)
await payload.find({
  collection: 'posts',
  where: {
    createdAt: { greater_than: lastCursor },
  },
  limit: 20,
  sort: 'createdAt',
})

Sorting

// Single field
sort: 'createdAt'      // Ascending
sort: '-createdAt'     // Descending

// Multiple fields
sort: ['-featured', '-createdAt']

Access Control in Local API

CRITICAL: Local API bypasses access control by default!

// ❌ INSECURE: Access control bypassed
await payload.find({
  collection: 'posts',
  user: someUser, // User is ignored!
})

// ✅ SECURE: Access control enforced
await payload.find({
  collection: 'posts',
  user: someUser,
  overrideAccess: false, // REQUIRED
})

REST API

Endpoints

GET    /api/{collection}              # Find
GET    /api/{collection}/{id}         # Find by ID
POST   /api/{collection}              # Create
PATCH  /api/{collection}/{id}         # Update
DELETE /api/{collection}/{id}         # Delete

Query String

GET /api/posts?where[status][equals]=published&limit=10&sort=-createdAt&depth=2

Nested Queries

GET /api/posts?where[author.name][contains]=John

Complex Queries

GET /api/posts?where[or][0][status][equals]=published&where[or][1][author][equals]=123

GraphQL API

Query

query {
  Posts(
    where: { status: { equals: published } }
    limit: 10
    sort: "-createdAt"
  ) {
    docs {
      id
      title
      author {
        name
      }
    }
    totalDocs
  }
}

Mutation

mutation {
  createPost(data: { title: "New Post", status: draft }) {
    id
    title
  }
}

Draft Queries

// Published only (default)
await payload.find({ collection: 'posts' })

// Include drafts
await payload.find({
  collection: 'posts',
  draft: true,
})

Count Only

const count = await payload.count({
  collection: 'posts',
  where: { status: { equals: 'published' } },
})
// Returns: { totalDocs: number }

Distinct Values

const categories = await payload.find({
  collection: 'posts',
  select: { category: true },
  // Then dedupe in code
})

Performance Tips

  1. Use indexes - Add index: true to frequently queried fields
  2. Limit depth - Lower depth = faster queries
  3. Select specific fields - Don't fetch what you don't need
  4. Use pagination - Never fetch all documents
  5. Avoid nested OR queries - Can be slow on large collections
  6. Use count for totals - Faster than fetching all docs