API Extensions

Event Hooks

Hooks allow running code when during the Directus lifecycle or database events.

Hooks allow running code when events occur within Directus. Events are triggered on schedules, database events, schedules, or during the Directus application lifecycle.

This extension type is loaded into the Directus process. They can use the provided services exported by the @directus/extensions-sdk package and can be written in JavaScript or TypeScript.

Hook Entrypoint

The index.js or index.ts file exports a function that is read by Directus. It contains one or more event listeners.

Entrypoint Example

export default ({ filter, action }) => {
    filter('items.create', () => {
        console.log('Creating Item!');
    });

    action('items.create', () => {
        console.log('Item created!');
    });
};

Register Function

The register function receives an object containing the type-specific register functions as the first parameter:

TypeDescription
filterHappens before the event is emitted. Use to check, modify, or prevent the event from being emitted.
actionHappens after the event is emitted. Use to execute further logic, enrichment, or automations.
initHappens at a defined point within the lifecycle of Directus.
scheduleHappens on a defined time interval.
embedInject custom JavaScript or CSS in the Data Studio.

The second parameter is a context object with the following properties:

PropertyDescription
servicesAll internal Directus services.
databaseKnex instance that is connected to the current database
getSchemaAsync function that reads the full available schema for use in services
envParsed environment variables.
loggerPino instance
emitterEvent emitter instance that can be used to emit custom events for other extensions

Filter

Filter events are called before an event is emitted.

export default ({ filter }) => {
    filter('items.create', (payload, meta, context) => {
        console.log('About to create item.');
    return payload;
    });
}

The filter register function takes an event name and a callback function that receives the modifiable payload, an event-specific meta object, and a context object when the event is emitted.

The meta object contains the following properties:

PropertyDescription
eventThe type of event that is being emitted.
collectionThe collection where the event is occuring.

The context object has the following properties:

PropertyDescription
databaseThe current database transaction.
schemaThe current API schema in use.
accountabilityInformation about the current user.

Filter Events

NamePayloadMeta
websocket.messageThe message sent over the WebSocket.
request.not_foundfalserequest, response
request.errorThe request errors.
database.errorThe database error.client
auth.loginThe login payload.status, user, provider
auth.jwtThe auth token.status, user, provider, type
auth.create1The created user.identifier, provider, providerPayload
auth.update2The updated auth token3.identifier, provider, providerPayload
authenticateThe empty accountability object.req
email.sendThe email payload.
(<collection>.)items.queryThe items query.collection
(<collection>.)items.readThe read item.query, collection
(<collection>.)items.createThe new item.collection
(<collection>.)items.updateThe updated item.keys, collection
(<collection>.)items.promoteThe promoted item.collection, item, version
(<collection>.)items.deleteThe keys of the item.collection
<system-collection>.queryThe items query.collection
<system-collection>.readThe read item.query, collection
<system-collection>.createThe new item.collection
<system-collection>.updateThe updated item.keys, collection
<system-collection>.deleteThe keys of the item.collection

1 Available for the ldap, oauth2, openid and saml driver.

2 Available for the ldap, oauth2, and openid driver.

3 Available for the oauth2, and openid driver if set by provider.

System Collection Exceptions
The collections and fields system collections do not emit a read event. The files collection does not emit a create or update event on file upload. The relations collection does not emit a delete event.

Directus reads system collections to operate. Be careful when modifying the output of system collection read or query events. Also ensure not to directly or indirectly emit the same event your hook is handling or you will create an infinite loop.

Filters are Blocking Filters can impact performance if not implemented carefully, especially on read events, which can lead to many database reads.

Action

Actions events are called after an event is emitted.

export default ({ action }) => {
    action('items.create', (meta, context) => {
        console.log('Item was just created.');
    });
}

The action register function takes an event name and a callback function that receives a meta object (containing information about the action and the payload) and a context object.

The meta object contains the following properties:

PropertyDescription
eventThe type of event that was emitted.
payloadThe data associated with the event.
keyThe primary key of the item.
collectionThe collection where the event occurred.

The context object has the following properties:

PropertyDescription
databaseThe current database transaction.
schemaThe current API schema in use.
accountabilityInformation about the current user.

Action Events

NameMeta
websocket.messagemessage, client
websocket.errorclient, event
websocket.closeclient, event
websocket.connectclient
websocket.auth.successclient
websocket.auth.failureclient
server.startserver
server.stopserver
responserequest, response, ip, duration, finished
auth.loginpayload, status, user, provider
files.uploadpayload, key, collection
(<collection>.)items.readpayload, query, collection
(<collection>.)items.createpayload, key, collection
(<collection>.)items.updatepayload, keys, collection
(<collection>.)items.promotepayload, collection, item, version
(<collection>.)items.deletekeys, collection
(<collection>.)items.sortcollection, item, to
<system-collection>.readpayload, query, collection
<system-collection>.createpayload, key, collection
<system-collection>.updatepayload, keys, collection
<system-collection>.deletekeys, collection
System Collection Exceptions
The collections and fields system collections do not emit a read event. The files collection does not emit a create or update event on file upload. The relations collection does not emit a delete event.

Directus reads system collections to operate. Be careful when modifying the output of system collection read or query events. Also ensure not to directly or indirectly emit the same event your hook is handling or you will create an infinite loop.

Init

Init events are called during the Directus application lifecycle.

export default ({ init }) => {
    init('routes.before', (meta) => {
        console.log(meta);
    });
};

The init register function takes an event name and a callback function that receives meta. meta contains either program or app (the full underlying Express application) depending on the lifecycle event.

Init Events

NameMeta
cli.beforeprogram
cli.afterprogram
app.beforeapp
app.afterapp
routes.beforeapp
routes.afterapp
routes.custom.beforeapp
routes.custom.afterapp
middlewares.beforeapp
middlewares.afterapp

Schedule

Schedule events are called on a defined time interval.

export default ({ schedule }) => {
    schedule('*/15 * * * *', () => {
        console.log('15 minutes have passed.');
    });
};

The schedule event takes a cron string as the first argument and a callback function as the second argument. The cron string follows the following format:

*    *    *    *    *    *
┬    ┬    ┬    ┬    ┬    ┬
│    │    │    │    │    │
│    │    │    │    │    └ day of week (0 - 7) (0 or 7 is Sun)
│    │    │    │    └───── month (1 - 12)
│    │    │    └────────── day of month (1 - 31)
│    │    └─────────────── hour (0 - 23)
│    └──────────────────── minute (0 - 59)
└───────────────────────── second (0 - 59, OPTIONAL)

Embed

The embed hook injects custom JavaScript or CSS into the <head> and <body> tags within the Data Studio.

export default ({ embed }) => {
    embed('body', '<script>console.log("Hello World")</script>');
};

The embed register function requires two parameters - the position of the embed (either head or body), and a value to embed (either a string or a function that returns a string).

Sandboxed Hooks

When using the sandbox, you have access to filter and action events only. Callback functions recieve the payload object as the only parameter.

TypeScript

You can import the SandboxHookRegisterContext type from directus:api to type the register function's context object:

/// <reference types="@directus/extensions/api.d.ts" />
import type { SandboxHookRegisterContext } from 'directus:api';

export default ({ filter, action }: SandboxHookRegisterContext) => {
};
Learn more about the Directus sandbox for API extensions.

Using Directus Internals

To access systems like permission checks and your collections, you can use internal Directus services, available through an API extension's context parameter.

Learn more about using internal Directus services.

Error Handling

To create errors in API extensions, you can utilize the @directus/errors package which is available to all extensions without installation.

import { createError } from '@directus/errors';

const ForbiddenError = createError('FORBIDDEN', "You don't have permissions to see this.", 403);

throw new ForbiddenError();