Add configuration for BMad, Claude, OpenCode, and other AI agent tools and workflows.
359 lines
5.7 KiB
Markdown
359 lines
5.7 KiB
Markdown
# Queries Reference
|
|
|
|
## Local API
|
|
|
|
### Find Multiple
|
|
|
|
```ts
|
|
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
|
|
|
|
```ts
|
|
const post = await payload.findByID({
|
|
collection: 'posts',
|
|
id: '123',
|
|
depth: 2,
|
|
})
|
|
```
|
|
|
|
### Create
|
|
|
|
```ts
|
|
const newPost = await payload.create({
|
|
collection: 'posts',
|
|
data: {
|
|
title: 'New Post',
|
|
content: '...',
|
|
author: userId,
|
|
},
|
|
user: req.user, // For access control
|
|
})
|
|
```
|
|
|
|
### Update
|
|
|
|
```ts
|
|
const updated = await payload.update({
|
|
collection: 'posts',
|
|
id: '123',
|
|
data: {
|
|
title: 'Updated Title',
|
|
},
|
|
})
|
|
```
|
|
|
|
### Delete
|
|
|
|
```ts
|
|
const deleted = await payload.delete({
|
|
collection: 'posts',
|
|
id: '123',
|
|
})
|
|
```
|
|
|
|
## Query Operators
|
|
|
|
### Comparison
|
|
|
|
```ts
|
|
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
|
|
|
|
```ts
|
|
where: {
|
|
title: { like: 'Hello' }, // Case-insensitive contains
|
|
title: { contains: 'world' }, // Case-sensitive contains
|
|
email: { exists: true }, // Field has value
|
|
}
|
|
```
|
|
|
|
### Array Operations
|
|
|
|
```ts
|
|
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
|
|
|
|
```ts
|
|
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:
|
|
|
|
```ts
|
|
where: {
|
|
'author.name': { contains: 'John' },
|
|
'category.slug': { equals: 'tech' },
|
|
}
|
|
```
|
|
|
|
### Geospatial Queries
|
|
|
|
```ts
|
|
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:
|
|
|
|
```ts
|
|
const posts = await payload.find({
|
|
collection: 'posts',
|
|
select: {
|
|
title: true,
|
|
slug: true,
|
|
author: true, // Will be populated based on depth
|
|
},
|
|
})
|
|
```
|
|
|
|
## Depth (Relationship Population)
|
|
|
|
```ts
|
|
// 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
|
|
|
|
```ts
|
|
// 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
|
|
|
|
```ts
|
|
// 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!**
|
|
|
|
```ts
|
|
// ❌ 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
|
|
|
|
```graphql
|
|
query {
|
|
Posts(
|
|
where: { status: { equals: published } }
|
|
limit: 10
|
|
sort: "-createdAt"
|
|
) {
|
|
docs {
|
|
id
|
|
title
|
|
author {
|
|
name
|
|
}
|
|
}
|
|
totalDocs
|
|
}
|
|
}
|
|
```
|
|
|
|
### Mutation
|
|
|
|
```graphql
|
|
mutation {
|
|
createPost(data: { title: "New Post", status: draft }) {
|
|
id
|
|
title
|
|
}
|
|
}
|
|
```
|
|
|
|
## Draft Queries
|
|
|
|
```ts
|
|
// Published only (default)
|
|
await payload.find({ collection: 'posts' })
|
|
|
|
// Include drafts
|
|
await payload.find({
|
|
collection: 'posts',
|
|
draft: true,
|
|
})
|
|
```
|
|
|
|
## Count Only
|
|
|
|
```ts
|
|
const count = await payload.count({
|
|
collection: 'posts',
|
|
where: { status: { equals: 'published' } },
|
|
})
|
|
// Returns: { totalDocs: number }
|
|
```
|
|
|
|
## Distinct Values
|
|
|
|
```ts
|
|
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
|