API Extensions
Services
When building extensions, you may use internal Directus services directly.
API extensions can directly use internal Directus services like the ItemsService
, CollectionsService
, FilesService
, and more, accessible through the context
parameter.
When initializing services, you will need the following:
Parameter | Description |
---|---|
schema | Knex database schema, provided by the getSchema function. |
accountability | Accountability object, used for access control. Omission will use administrator permissions. null will use public permissions. |
This page uses endpoints to demonstrate how to use services, but they can also be used in other API extension types.
Infinite Recursion
If you are listening to an event and your handler is emitting the same event, you will need to pass
{ emitEvents: false }
in order to prevent infinite recursion.ItemsService
export default (router, context) => {
const { services, getSchema } = context;
const { ItemsService } = services;
router.get('/', async (req, res) => {
const itemsService = new ItemsService('collection_name', {
schema: await getSchema(),
accountability: req.accountability
});
// Your route handler logic
});
};
Create an Item
const data = await itemsService.createOne({
title: 'Hello world!',
body: 'This is our first article',
});
Read an Item
const data = await itemsService.readOne('item_id');
Update an Item
const data = await itemsService.updateOne('item_id', {
title: "An updated title"
});
Delete an Item
const data = await itemsService.deleteOne('item_id');
CollectionsService
export default (router, context) => {
const { services, getSchema } = context;
const { CollectionsService } = services;
router.get('/', async (req, res) => {
const collectionsService = new CollectionsService({
schema: await getSchema(),
accountability: req.accountability
});
// Your route handler logic
});
};
Create a Collection
const data = await collectionsService.createOne({
name: 'articles',
meta: {
note: 'Blog posts.',
},
});
Read a Collection
const data = await collectionsService.readOne('collection_name');
Update a Collection
const data = await collectionsService.updateOne('collection_name', {
meta: {
note: 'Updated blog posts.',
},
});
Delete a Collection
const data = await collectionsService.deleteOne('collection_name');
See a complete list of all methods in the
CollectionsService
by looking at the Directus source code.FieldsService
export default (router, context) => {
const { services, getSchema } = context;
const { FieldsService } = services;
router.get('/', async (req, res) => {
const fieldsService = new FieldsService({
schema: await getSchema(),
accountability: req.accountability
});
// Your route handler logic
});
}
Create a Field
await fieldsService.createField('collection_name', {
field: 'title',
type: 'string',
meta: {
icon: 'title',
},
schema: {
default_value: 'Hello World',
},
});
Read a Field
const data = await fieldsService.readOne('collection_name', 'field_name');
Update a Field
const data = await fieldsService.updateField('collection_name', {
field: 'field_name',
meta: {
icon: 'title',
},
});
You cannot update the field name via Directus after creating the field.
Delete a Field
const data = await fieldsService.deleteField('collection_name', 'field_name');
RelationsService
export default (router, context) => {
const { services, getSchema } = context;
const { RelationsService } = services;
router.get('/', async (req, res) => {
const relationsService = new RelationsService({
schema: await getSchema(),
accountability: req.accountability
});
// Your route handler logic
});
};
Create a Relation
const data = await relationsService.createOne({
collection: 'articles',
field: 'featured_image',
related_collection: 'directus_files',
});
Read a Relation
const data = await relationsService.readOne('collection_name', 'field_name');
Update a Relation
const data = await relationsService.updateOne(
'collection_name',
'field_name',
{
meta: {
one_field: 'articles',
},
},
);
Delete a Relation
const data = await relationsService.deleteOne('collection_name', 'field_name' );
FilesService
export default (router, context) => {
const { services, getSchema } = context;
const { FilesService } = services;
router.get('/', async (req, res) => {
const filesService = new FilesService({
schema: await getSchema(),
accountability: req.accountability
});
// Your route handler logic
});
};
Import a File
const assetKey = await filesService.importOne(file_url, file_object);
Upload a File
Uploading a file requires the use of an external dependency called Busboy, a streaming parser for Node.js. Import it at the top of your extension:
import Busboy from 'busboy'
Then, inside of the route handler, pipe the request into Busboy:
const busboy = Busboy({ headers: req.headers });
busboy.on('file', async (_, fileStream, { filename, mimeType }) => {
const primaryKey = await filesService.uploadOne(fileStream, {
filename_download: filename,
type: mimeType,
storage: 'local',
});
});
req.pipe(busboy);
Read a File
const data = await filesService.readOne('file_id');
Update a File
const data = await filesService.updateOne('file_id', { title: 'Random' });
Delete a File
const data = await filesService.deleteOne('file_id');
Get once-a-month release notes & real‑world code tips...no fluff. 🐰