Event Hooks
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:
Type | Description |
---|---|
filter | Happens before the event is emitted. Use to check, modify, or prevent the event from being emitted. |
action | Happens after the event is emitted. Use to execute further logic, enrichment, or automations. |
init | Happens at a defined point within the lifecycle of Directus. |
schedule | Happens on a defined time interval. |
embed | Inject custom JavaScript or CSS in the Data Studio. |
The second parameter is a context object with the following properties:
Property | Description |
---|---|
services | All internal Directus services. |
database | Knex instance that is connected to the current database |
getSchema | Async function that reads the full available schema for use in services |
env | Parsed environment variables. |
logger | Pino instance |
emitter | Event 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:
Property | Description |
---|---|
event | The type of event that is being emitted. |
collection | The collection where the event is occuring. |
The context object has the following properties:
Property | Description |
---|---|
database | The current database transaction. |
schema | The current API schema in use. |
accountability | Information about the current user. |
Filter Events
Name | Payload | Meta |
---|---|---|
websocket.message | The message sent over the WebSocket. | |
request.not_found | false | request , response |
request.error | The request errors. | |
database.error | The database error. | client |
auth.login | The login payload. | status , user , provider |
auth.jwt | The auth token. | status , user , provider , type |
auth.create 1 | The created user. | identifier , provider , providerPayload |
auth.update 2 | The updated auth token3. | identifier , provider , providerPayload |
authenticate | The empty accountability object. | req |
email.send | The email payload. | |
(<collection>.)items.query | The items query. | collection |
(<collection>.)items.read | The read item. | query , collection |
(<collection>.)items.create | The new item. | collection |
(<collection>.)items.update | The updated item. | keys , collection |
(<collection>.)items.promote | The promoted item. | collection , item , version |
(<collection>.)items.delete | The keys of the item. | collection |
<system-collection>.query | The items query. | collection |
<system-collection>.read | The read item. | query , collection |
<system-collection>.create | The new item. | collection |
<system-collection>.update | The updated item. | keys , collection |
<system-collection>.delete | The 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.
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.
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:
Property | Description |
---|---|
event | The type of event that was emitted. |
payload | The data associated with the event. |
key | The primary key of the item. |
collection | The collection where the event occurred. |
The context object has the following properties:
Property | Description |
---|---|
database | The current database transaction. |
schema | The current API schema in use. |
accountability | Information about the current user. |
Action Events
Name | Meta |
---|---|
websocket.message | message , client |
websocket.error | client , event |
websocket.close | client , event |
websocket.connect | client |
websocket.auth.success | client |
websocket.auth.failure | client |
server.start | server |
server.stop | server |
response | request , response , ip , duration , finished |
auth.login | payload , status , user , provider |
files.upload | payload , key , collection |
(<collection>.)items.read | payload , query , collection |
(<collection>.)items.create | payload , key , collection |
(<collection>.)items.update | payload , keys , collection |
(<collection>.)items.promote | payload , collection , item , version |
(<collection>.)items.delete | keys , collection |
(<collection>.)items.sort | collection , item , to |
<system-collection>.read | payload , query , collection |
<system-collection>.create | payload , key , collection |
<system-collection>.update | payload , keys , collection |
<system-collection>.delete | keys , collection |
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
Name | Meta |
---|---|
cli.before | program |
cli.after | program |
app.before | app |
app.after | app |
routes.before | app |
routes.after | app |
routes.custom.before | app |
routes.custom.after | app |
middlewares.before | app |
middlewares.after | app |
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) => {
};
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.
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();