Flows

All flows have data from the following items

  • $trigger
  • Each operation’s id

To access it, you can use mustache:

  "{{ $trigger.body.name }}"
  

Or for scripts, use data

  data.$trigger.body.name
  

Example JSON

  {
    "$trigger": {
        "path": "/trigger/3ba54f5a-5525-4bd0-8cd8-df4b143f08e5",
        "query": {},
        "body": {
            "name": "ty",
            "unique_count": 1,
            "search_count": 1
        },
        "method": "POST",
        "headers": {
            "host": "hostname",
            "user-agent": "my_agent/2023.2.2",
            "content-length": "57",
            "accept": "*/*",
            "accept-encoding": "gzip",
            "authorization": "Bearer token",
            "cdn-loop": "cloudflare",
            "cf-connecting-ip": "ip_addr",
            "cf-ipcountry": "US",
            "cf-ray": "6e55ed5c5a2306a7-NND",
            "cf-visitor": "{\"scheme\":\"https\"}",
            "content-type": "application/json",
            "x-forwarded-for": "ip_addr",
            "x-forwarded-host": "hostname",
            "x-forwarded-proto": "https"
        }
    },
    "$last": [
        "ty"
    ],
    "$accountability": {
        "user": "569eae4a-f905-4fdf-bdfc-35bb8db31852",
        "role": "d779759d-d4cc-40f3-912a-a17a4e83f015",
        "admin": true,
        "app": true,
        "ip": "ip_addr",
        "userAgent": "agent/2023.2.2",
        "permissions": []
    },
    "$env": {},
    "query_id": [
        {
            "name": "user",
            "date_updated": "2023-07-12T02:41:24.719Z",
            "config": null
        }
    ],
    "item_update_joye2": [
        "user"
    ]
}
  

Dynamic values

Remove older then 5 years

  {
    "filter": {
        "initiationtimestamp": {
            "_lt": "$NOW(-5 years)"
        }
    }
}
  

Read Database

When using the read database operation, it will return an array.

data is the flow object. So anything in there can be retrieved from.

For updating the database (or anything with Payload), make sure to edit the payload in raw form only, and then insert the variable name.

  "{{data_variable}}"
  

If you save and re-enter and it has it escaped, you need to remove and do it again in the raw mode.

Queries

The query is effectively the search query, but in JSON. query - filter rules

  [aggregate][count]=id
{
    "aggregate": {
        "count": "id"
    }
}
  

To do multiple values:

  {
    "aggregate": {
        "count": [
            "id",
            "name2"
        ]
    }
}
  

Run Script

This example checks the data from a read operation, see’s if it is 1 for that field, and then increments it if true.

Example:

  module.exports = async function(data) {
    let unique_count = data.read_ahc_data[0].unique_count;
    let search_count = data.read_ahc_data[0].search_count;
    if(data.$trigger.body.unique_count === 1) {
        unique_count += 1
    }
    if(data.$trigger.body.search_count === 1) {
        search_count += 1
    }

    return {
       name: data.$trigger.body.name,
       unique_count: unique_count,
       search_count: search_count
    };
}
  

Aggregation

You can aggregate with the aggregate query

For example, to count the number of id records

  https://directus.dev.lan/items/users?aggregate[count]=id
  

Custom Extension

Docker Structure for endpoint /login

  drwxr-xr-x   1000:1000     296 MB  ├── /directus
-rwxr-xr-x   1000:1000      437 B  │   ├── cli.js
drwxr-xr-x   1000:1000        0 B  │   ├── database
drwxr-xr-x   1000:1000       77 B  │   ├── extensions
drwxr-xr-x         0:0       77 B  │   │   └── endpoints
drwxr-xr-x         0:0       77 B  │   │       └── login
-rw-r--r--         0:0       77 B  │   │           └── index.js
  

To create a new extension

  npm init directus-extension
vi new_extension/src/index.js
  
  // index.js 
export default (router) => {  
  router.get('/', (req, res) => res.send('Hello, World!'));
};
  

Querying with JSON

It can use JSON, but you have to specify each section (instead of one gigantic query operator (which may not exist))

  filter={ "initiationmethod": { "_eq": "INBOUND" }}
aggregate={ "count": "id" }
fields=id
  

Probably this page for each top level can be included

So a query like this would work

  domain/items/aggregate={ "count": "id" }&filter={ "initiationmethod": { "_eq": "INBOUND" }}
  

Extension

Endpoint

  export default defineEndpoint((router, context) => {
  const { services, getSchema } = context;
  const { ItemsService } = services;

  router.get('/', async (req, res) => {
}
  

## Hook

Action

create

  export default ({ action }, { env, services }) => {
	const { MailService, ItemsService } = services;

	action('mycollection.items.create', async ({ key, collection, payload }, { schema }) => {
    }
}
  

Filter

create

  export default ({ filter }, { env }) => {
	filter('items.create', async (input, { collection }) => {
    }
}
  

update

  export default ({ filtjj/filterer }, {  }
  

delete

  
	filter(`${COLLECTION}.items.delete`, async ( keys , meta, obj3, obj4) => {
  

readQuery

      const roles = await rolesService.readByQuery({
        fields: ['id', 'name'],
        filter: { name: { _eq: name } }
    })
     if(roles.length !== 1) {
        throw new Error("Can't be more than 1 role for the specified name, or it's missing")
    }
    return roles[0]
  

Emails

MailService uses NodeMailer to work. Nodemailer send config
This passes everything directly to nodemailer’s sendMail

  await mailService.send({})
  

Icons

To use icons, you can use this library

HTTP Query fields and filtering

Fields

This will get all items and their sub items. Not optimized. You can also get item.subitem (like user.policy.id)

  fields=*.*,item.subitem
  

Filter Only show items of contact not null

  filter[contact][_nnull]
  

You can also combine them with OR or AND statement

  filter[_or][1][contact][_null]
filter[_or][1][email_address][_null]
  

Resources