content/guides/09.extensions/2.api-extensions/1.hooks.md
Hooks allow running code when events occur within Directus. Events are triggered on schedules, database events, schedules, or during the Directus application lifecycle.
:partial{content="extensions-api"}
The index.js or index.ts file exports a function that is read by Directus. It contains one or more event listeners.
export default ({ filter, action }) => {
filter("items.create", () => {
console.log("Creating Item!");
});
action("items.create", () => {
console.log("Item created!");
});
};
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 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. |
| Name | Payload | Meta |
|---|---|---|
websocket.authenticate | The default accountability object. | message |
websocket.message | The message sent over the WebSocket. | client |
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, error |
auth.jwt | The auth token. | status, user, provider, type |
auth.create<sup>[1]</sup> | The created user. | identifier, provider, providerPayload |
auth.update<sup>[2]</sup> | The updated auth token<sup>[3]</sup>. | identifier, provider, providerPayload |
authenticate | The default 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 |
.query | The items query. | collection |
.read | The read item. | query, collection |
.create | The new item. | collection |
.update | The updated item. | keys, collection |
.delete | The keys of the item. | collection |
<sup>[1]</sup> Available for the ldap, oauth2, openid and saml driver.
<sup>[2]</sup> Available for the ldap, oauth2, and openid driver.
<sup>[3]</sup> Available for the oauth2, and openid driver if set by provider.
:partial{content="extension-hook-footguns"}
::callout{icon="material-symbols:warning-rounded" color="warning"}
Filters are Blocking
Filters can impact performance if not implemented carefully, especially on read events, which can lead to many database reads.
::
:partial{content="extension-hook-system-collections"}
Action 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. |
| 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 |
extensions.load | extensions |
extensions.unload | extensions |
extensions.reload | extensions, added , removed |
extensions.installed | extensions, versionId |
extensions.uninstalled | extensions, folder |
(<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 |
.read | payload, query, collection |
.create | payload, key, collection |
.update | payload, keys, collection |
.delete | keys, collection |
::callout{icon="material-symbols:menu-book-outline" color="primary" to="/guides/realtime/custom-handlers"} Learn how to build custom WebSocket handlers with practical examples using these hooks. ::
:partial{content="extension-hook-footguns"}
:partial{content="extension-hook-system-collections"}
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.
| 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 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)
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).
When using the sandbox, you have access to filter and action events only. Callback functions recieve the payload object as the only parameter.
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) => {};
::callout{icon="material-symbols:menu-book-outline" color="primary" to="/guides/extensions/api-extensions/sandbox"} Learn more about the Directus sandbox for API extensions. ::
:partial{content="extensions-api-internals"}