content/guides/09.extensions/2.api-extensions/4.services.md
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 | The database schema, provided by the getSchema function. |
accountability | Accountability object, used for access control. Omission or null will use administrator permissions. |
::callout{icon="material-symbols:link" to="https://github.com/directus/directus/tree/main/api/src/services"} See a complete list of all services available by looking at the Directus source code. ::
This page uses endpoints to demonstrate how to use services, but they can also be used in other API extension types.
::callout{icon="material-symbols:warning-rounded" color="warning"}
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.
::
ItemsServiceexport 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
});
};
const data = await itemsService.createOne({
title: 'Hello world!',
body: 'This is our first article',
});
const data = await itemsService.readOne('item_id');
const data = await itemsService.updateOne('item_id', {
title: "An updated title"
});
const data = await itemsService.deleteOne('item_id');
::callout{icon="material-symbols:link" to="https://github.com/directus/directus/blob/main/api/src/services/items.ts"}
See a complete list of all methods in the ItemsService by looking at the Directus source code.
::
CollectionsServiceexport 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
});
};
const data = await collectionsService.createOne({
name: 'articles',
meta: {
note: 'Blog posts.',
},
});
const data = await collectionsService.readOne('collection_name');
const data = await collectionsService.updateOne('collection_name', {
meta: {
note: 'Updated blog posts.',
},
});
const data = await collectionsService.deleteOne('collection_name');
::callout{icon="material-symbols:link" to="https://github.com/directus/directus/blob/main/api/src/services/collections.ts"}
See a complete list of all methods in the CollectionsService by looking at the Directus source code.
::
FieldsServiceexport 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
});
}
await fieldsService.createField('collection_name', {
field: 'title',
type: 'string',
meta: {
icon: 'title',
},
schema: {
default_value: 'Hello World',
},
});
const data = await fieldsService.readOne('collection_name', 'field_name');
const data = await fieldsService.updateField('collection_name', {
field: 'field_name',
meta: {
icon: 'title',
},
});
::callout{icon="material-symbols:info-outline"} You cannot update the field name via Directus after creating the field. ::
const data = await fieldsService.deleteField('collection_name', 'field_name');
RelationsServiceexport 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
});
};
const data = await relationsService.createOne({
collection: 'articles',
field: 'featured_image',
related_collection: 'directus_files',
});
const data = await relationsService.readOne('collection_name', 'field_name');
const data = await relationsService.updateOne(
'collection_name',
'field_name',
{
meta: {
one_field: 'articles',
},
},
);
const data = await relationsService.deleteOne('collection_name', 'field_name' );
::callout{icon="material-symbols:link" to="https://github.com/directus/directus/blob/main/api/src/services/relations.ts"}
See a complete list of all methods in the RelationsService by looking at the Directus source code.
::
FilesServiceexport 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
});
};
const assetKey = await filesService.importOne(file_url, file_object);
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);
const data = await filesService.readOne('file_id');
const data = await filesService.updateOne('file_id', { title: 'Random' });
const data = await filesService.deleteOne('file_id');
::callout{icon="material-symbols:link" to="https://github.com/directus/directus/blob/main/api/src/services/files.ts"}
See a complete list of all methods in the FilesService by looking at the Directus source code.
::