224 lines
4.1 KiB
Markdown
224 lines
4.1 KiB
Markdown
---
|
|
title: Queries
|
|
description: Local API, REST, and GraphQL query patterns
|
|
tags: [payload, queries, local-api, rest, graphql]
|
|
---
|
|
|
|
# Payload CMS Queries
|
|
|
|
## Query Operators
|
|
|
|
```typescript
|
|
// Equals
|
|
{ color: { equals: 'blue' } }
|
|
|
|
// Not equals
|
|
{ status: { not_equals: 'draft' } }
|
|
|
|
// Greater/less than
|
|
{ price: { greater_than: 100 } }
|
|
{ age: { less_than_equal: 65 } }
|
|
|
|
// Contains (case-insensitive)
|
|
{ title: { contains: 'payload' } }
|
|
|
|
// Like (all words present)
|
|
{ description: { like: 'cms headless' } }
|
|
|
|
// In/not in
|
|
{ category: { in: ['tech', 'news'] } }
|
|
|
|
// Exists
|
|
{ image: { exists: true } }
|
|
|
|
// Near (point fields)
|
|
{ location: { near: [10, 20, 5000] } } // [lng, lat, maxDistance]
|
|
```
|
|
|
|
## AND/OR Logic
|
|
|
|
```typescript
|
|
{
|
|
or: [
|
|
{ color: { equals: 'mint' } },
|
|
{
|
|
and: [
|
|
{ color: { equals: 'white' } },
|
|
{ featured: { equals: false } },
|
|
],
|
|
},
|
|
],
|
|
}
|
|
```
|
|
|
|
## Nested Properties
|
|
|
|
```typescript
|
|
{
|
|
'author.role': { equals: 'editor' },
|
|
'meta.featured': { exists: true },
|
|
}
|
|
```
|
|
|
|
## Local API
|
|
|
|
```typescript
|
|
// Find documents
|
|
const posts = await payload.find({
|
|
collection: 'posts',
|
|
where: {
|
|
status: { equals: 'published' },
|
|
'author.name': { contains: 'john' },
|
|
},
|
|
depth: 2, // Populate relationships
|
|
limit: 10,
|
|
page: 1,
|
|
sort: '-createdAt',
|
|
locale: 'en',
|
|
select: {
|
|
title: true,
|
|
author: true,
|
|
},
|
|
})
|
|
|
|
// Find by ID
|
|
const post = await payload.findByID({
|
|
collection: 'posts',
|
|
id: '123',
|
|
depth: 2,
|
|
})
|
|
|
|
// Create
|
|
const post = await payload.create({
|
|
collection: 'posts',
|
|
data: {
|
|
title: 'New Post',
|
|
status: 'draft',
|
|
},
|
|
})
|
|
|
|
// Update
|
|
await payload.update({
|
|
collection: 'posts',
|
|
id: '123',
|
|
data: {
|
|
status: 'published',
|
|
},
|
|
})
|
|
|
|
// Delete
|
|
await payload.delete({
|
|
collection: 'posts',
|
|
id: '123',
|
|
})
|
|
|
|
// Count
|
|
const count = await payload.count({
|
|
collection: 'posts',
|
|
where: {
|
|
status: { equals: 'published' },
|
|
},
|
|
})
|
|
```
|
|
|
|
## Access Control in Local API
|
|
|
|
**CRITICAL**: Local API bypasses access control by default (`overrideAccess: true`).
|
|
|
|
```typescript
|
|
// ❌ WRONG: User is passed but access control is bypassed
|
|
const posts = await payload.find({
|
|
collection: 'posts',
|
|
user: currentUser,
|
|
// Result: Operation runs with ADMIN privileges
|
|
})
|
|
|
|
// ✅ CORRECT: Respects user's access control permissions
|
|
const posts = await payload.find({
|
|
collection: 'posts',
|
|
user: currentUser,
|
|
overrideAccess: false, // Required to enforce access control
|
|
})
|
|
|
|
// Administrative operation (intentionally bypass access control)
|
|
const allPosts = await payload.find({
|
|
collection: 'posts',
|
|
// No user parameter, overrideAccess defaults to true
|
|
})
|
|
```
|
|
|
|
**When to use `overrideAccess: false`:**
|
|
|
|
- Performing operations on behalf of a user
|
|
- Testing access control logic
|
|
- API routes that should respect user permissions
|
|
|
|
## REST API
|
|
|
|
```typescript
|
|
import { stringify } from 'qs-esm'
|
|
|
|
const query = {
|
|
status: { equals: 'published' },
|
|
}
|
|
|
|
const queryString = stringify(
|
|
{
|
|
where: query,
|
|
depth: 2,
|
|
limit: 10,
|
|
},
|
|
{ addQueryPrefix: true },
|
|
)
|
|
|
|
const response = await fetch(`https://api.example.com/api/posts${queryString}`)
|
|
const data = await response.json()
|
|
```
|
|
|
|
### REST Endpoints
|
|
|
|
```
|
|
GET /api/{collection} - Find documents
|
|
GET /api/{collection}/{id} - Find by ID
|
|
POST /api/{collection} - Create
|
|
PATCH /api/{collection}/{id} - Update
|
|
DELETE /api/{collection}/{id} - Delete
|
|
GET /api/{collection}/count - Count documents
|
|
|
|
GET /api/globals/{slug} - Get global
|
|
POST /api/globals/{slug} - Update global
|
|
```
|
|
|
|
## GraphQL
|
|
|
|
```graphql
|
|
query {
|
|
Posts(where: { status: { equals: published } }, limit: 10, sort: "-createdAt") {
|
|
docs {
|
|
id
|
|
title
|
|
author {
|
|
name
|
|
}
|
|
}
|
|
totalDocs
|
|
hasNextPage
|
|
}
|
|
}
|
|
|
|
mutation {
|
|
createPost(data: { title: "New Post", status: draft }) {
|
|
id
|
|
title
|
|
}
|
|
}
|
|
```
|
|
|
|
## Performance Best Practices
|
|
|
|
- Set `maxDepth` on relationships to prevent over-fetching
|
|
- Use `select` to limit returned fields
|
|
- Index frequently queried fields
|
|
- Use `virtual` fields for computed data
|
|
- Cache expensive operations in hook `context`
|