| name | sdk-patterns |
| description | @directus/sdk patterns — composable client, TypeScript types, CRUD operations, authentication, real-time subscriptions. This skill should be used when the user asks about "Directus SDK", "@directus/sdk", "Directus client library", "Directus TypeScript", or needs code patterns for integrating Directus into a JavaScript/TypeScript project. |
@directus/sdk Patterns
The official Directus SDK uses a composable architecture. Install:
npm install @directus/sdk
Client Setup
Basic Client (Static Token)
import { createDirectus, rest, staticToken } from '@directus/sdk';
const client = createDirectus('https://your-instance.com')
.with(staticToken('your-static-token'))
.with(rest());
Client with Login
import { createDirectus, rest, authentication } from '@directus/sdk';
const client = createDirectus('https://your-instance.com')
.with(authentication())
.with(rest());
await client.login('user@example.com', 'password');
Type-Safe Schema
Define your schema for TypeScript autocompletion:
interface Post {
id: string;
title: string;
content: string;
status: 'draft' | 'published' | 'archived';
author: string | Author;
categories: string[] | PostCategory[];
date_created: string;
}
interface Author {
id: string;
first_name: string;
last_name: string;
email: string;
}
interface Category {
id: string;
name: string;
slug: string;
}
interface PostCategory {
id: string;
posts_id: string | Post;
categories_id: string | Category;
}
interface MySchema {
posts: Post[];
authors: Author[];
: [];
: [];
}
client = createDirectus<>()
.(())
.(());
CRUD Operations
Read Items
import { readItems } from '@directus/sdk';
const posts = await client.request(
readItems('posts', {
fields: ['id', 'title', 'status', { author: ['first_name', 'last_name'] }],
filter: { status: { _eq: 'published' } },
sort: ['-date_created'],
limit: 25,
})
);
Read Single Item
import { readItem } from '@directus/sdk';
const post = await client.request(
readItem('posts', 'item-uuid', {
fields: ['*', { author: ['*'] }],
})
);
Create Item
import { createItem } from '@directus/sdk';
const newPost = await client.request(
createItem('posts', {
title: 'New Post',
content: 'Post content...',
status: 'draft',
author: 'author-uuid',
})
);
Create Multiple Items
import { createItems } from '@directus/sdk';
const newPosts = await client.request(
createItems('posts', [
{ title: 'Post 1', status: 'draft' },
{ title: 'Post 2', status: 'draft' },
])
);
Update Item
import { updateItem } from '@directus/sdk';
const updated = await client.request(
updateItem('posts', 'item-uuid', {
status: 'published',
})
);
Update Multiple Items
import { updateItems } from '@directus/sdk';
const updated = await client.request(
updateItems('posts', ['uuid-1', 'uuid-2'], {
status: 'archived',
})
);
Delete Item
import { deleteItem, deleteItems } from '@directus/sdk';
await client.request(deleteItem('posts', 'item-uuid'));
await client.request(deleteItems('posts', ['uuid-1', 'uuid-2']));
Filtering
const results = await client.request(
readItems('products', {
filter: {
_and: [
{ status: { _eq: 'active' } },
{
_or: [
{ price: { _lt: 50 } },
{ featured: { _eq: true } },
],
},
],
},
})
);
Aggregation
import { aggregate } from '@directus/sdk';
const stats = await client.request(
aggregate('orders', {
aggregate: { count: '*', sum: 'total', avg: 'total' },
groupBy: ['status'],
})
);
Search
const results = await client.request(
readItems('articles', {
search: 'machine learning',
fields: ['id', 'title', 'excerpt'],
limit: 20,
})
);
Deep Queries
const posts = await client.request(
readItems('posts', {
fields: ['title', { comments: ['text', { author: ['name'] }] }],
deep: {
comments: {
_filter: { status: { _eq: 'approved' } },
_sort: ['-date_created'],
_limit: 5,
},
},
})
);
File Operations
import { uploadFiles, importFile } from '@directus/sdk';
const formData = new FormData();
formData.append('file', fileBlob);
formData.append('title', 'My Image');
const uploaded = await client.request(uploadFiles(formData));
const imported = await client.request(
importFile('https://example.com/image.jpg', {
title: 'Imported Image',
folder: 'folder-uuid',
})
);
Schema Operations
import {
readCollections, createCollection,
readFields, createField,
readRelations, createRelation,
schemaSnapshot, schemaDiff, schemaApply,
} from '@directus/sdk';
const collections = await client.request(readCollections());
const snapshot = await client.request(schemaSnapshot());
const diff = await client.request(schemaDiff(snapshotFromStaging));
if (diff) {
await client.request(schemaApply(diff));
}
Real-Time (WebSocket)
import { createDirectus, realtime, staticToken } from '@directus/sdk';
const client = createDirectus('https://your-instance.com')
.with(staticToken('token'))
.with(realtime());
await client.connect();
const { subscription } = await client.subscribe('posts', {
event: 'create',
query: { fields: ['id', 'title', 'status'] },
});
for await (const event of subscription) {
console.log('New post:', event.data);
}
Error Handling
try {
const result = await client.request(readItem('posts', 'nonexistent'));
} catch (error) {
if (error.errors) {
for (const err of error.errors) {
console.error(`[${err.extensions?.code}] ${err.message}`);
}
}
}
Common error codes: FORBIDDEN, RECORD_NOT_UNIQUE, FAILED_VALIDATION, INVALID_PAYLOAD, ROUTE_NOT_FOUND.
Environment Variables
Always use env vars for configuration:
const client = createDirectus(process.env.DIRECTUS_URL!)
.with(staticToken(process.env.DIRECTUS_TOKEN!))
.with(rest());
DIRECTUS_URL=https://your-instance.com
DIRECTUS_TOKEN=your-static-token