Directus REST API / GraphQL API Setup
Directus automatically generates full REST API and GraphQL for all collections. REST is default, GraphQL is enabled in settings. Both use the same access control system.
REST API
Basic operations:
# List
GET /items/articles?fields=id,title,slug&filter[status][_eq]=published&sort=-date_published&limit=10
# Single item
GET /items/articles/42?fields=*,author.*,category.*
# Create
POST /items/articles
Authorization: Bearer <token>
{ "title": "New Article", "status": "draft" }
# Update
PATCH /items/articles/42
{ "status": "published" }
# Delete
DELETE /items/articles/42
Filters:
# Operators: _eq, _neq, _lt, _lte, _gt, _gte, _in, _nin, _null, _nnull, _contains, _icontains, _starts_with, _ends_with
# Numeric range
GET /items/products?filter[price][_gte]=100&filter[price][_lte]=500
# OR condition
GET /items/articles?filter[_or][0][title][_icontains]=react&filter[_or][1][content][_icontains]=react
# Related collections
GET /items/articles?filter[category][slug][_eq]=tech
# Multiple values
GET /items/orders?filter[status][_in]=paid,shipped
Populate (deep):
# All fields first level
GET /items/articles?fields=*
# Specific relation fields
GET /items/articles?fields=id,title,author.name,author.email,category.name,thumbnail.id,thumbnail.filename_disk
# Nested relations
GET /items/articles?fields=id,title,comments.id,comments.text,comments.user.name
# Deep populate
GET /items/articles/1?deep[comments][_sort]=-date_created&deep[comments][_limit]=5
Aggregation:
# Count, sum, average
GET /items/orders?aggregate[count]=*&aggregate[sum]=total&aggregate[avg]=total&filter[status][_eq]=paid
# Grouping
GET /items/orders?groupBy[]=status&aggregate[count]=*
System collections
# Users
GET /users?fields=id,first_name,last_name,email,role.name
# Files/media
GET /files?fields=id,filename_disk,width,height,filesize
# Activity
GET /activity?filter[collection][_eq]=articles&sort=-timestamp&limit=20
GraphQL
# Enable in .env
GRAPHQL_SDLFILE=/tmp/graphql-schema.graphql
GraphQL is available at /graphql:
query GetArticles($locale: String, $limit: Int) {
articles(
filter: { status: { _eq: "published" } }
sort: ["-date_published"]
limit: $limit
) {
id
title
slug
excerpt
date_published
thumbnail {
id
filename_disk
}
category {
name
slug
}
author {
first_name
last_name
}
}
}
query GetSingletonSettings {
settings {
site_name
site_description
logo { id filename_disk }
social_links
}
}
mutation CreateContact($data: create_contacts_input!) {
create_contacts_item(data: $data) {
id
email
message
}
}
Realtime via WebSocket
# Connection
ws://localhost:8055/websocket
# Authentication
{ "type": "auth", "access_token": "..." }
# Subscribe to collection
{ "type": "subscribe", "collection": "articles", "uid": "sub-1", "query": { "fields": ["id", "title"] } }
# Change event
{ "type": "subscription", "event": "update", "data": [{ "id": 1, "title": "Updated" }] }
Custom query via SDK
import { createDirectus, rest, aggregate, readItems } from '@directus/sdk'
const client = createDirectus(DIRECTUS_URL).with(rest())
// Sales aggregation for month
const salesStats = await client.request(
aggregate('orders', {
aggregate: { count: '*', sum: ['total'], avg: ['total'] },
groupBy: ['status'],
filter: { date_created: { _gte: '$NOW(-30 days)' } },
})
)
// Search with weights
const results = await client.request(
readItems('articles', {
search: 'TypeScript', // fulltext search across string fields
fields: ['id', 'title', 'excerpt'],
filter: { status: { _eq: 'published' } },
limit: 10,
})
)
Rate Limiting and security
# Rate limiting
RATE_LIMITER_ENABLED=true
RATE_LIMITER_STORE=redis
RATE_LIMITER_POINTS=50
RATE_LIMITER_DURATION=1
# Disable GraphQL introspection in production
GRAPHQL_INTROSPECTION=false
Timeline
Setting up GraphQL, aggregations, Realtime subscriptions — 1–2 days.







