How to use an external system’s ID (e.g. Lightspeed) as the primary key in Directus collection?

Hi all,

I’m integrating Directus with the Lightspeed Retail API and would like to use, for example, the categoryId from Lightspeed as the primary key (id) in my categories collection within Directus.

Here’s the flow I’m aiming for:

  1. A user creates a new category from the Directus Admin UI.
  2. A beforeCreate hook fires:
  • It creates the category in Lightspeed via their API.
  • It receives the Lightspeed categoryId.
  • It sets that categoryId as the id of the new Directus record.

The issue I’m running into is:

  • If the id field is not auto-generated, Directus requires you to provide an id before saving the record.
  • But I can’t know the id until after I call the Lightspeed API inside the hook.
  • This prevents me from using the Lightspeed categoryId as the Directus primary key.

An alternative would be to store the Lightspeed ID in a separate externalCategoryId field — but then I run into difficulties linking my items to categories, as Directus only supports relationships using the primary key (id).

Has anyone successfully implemented a setup like this, or found a clean workaround that keeps everything in sync?

Thanks in advance!

There are multiple ways you could approach this integration with Lightspeed.

Option 1: Flow with Filter

You could use a flow with a filter that blocks the ID or item from being created before calling the Lightspeed API.

Option 2: Custom Hook Extension (Recommended)

This is probably the route I’d go with for this use case.

Here’s how to set it up:

  1. Configure your collection’s primary key

    • When creating your new collection in Directus, don’t choose UUID or Integer as the primary key
    • Choose a manually entered string instead
    • This requires a manually entered string/ID before creating the record
  2. Build the hook logic

    • Inside your hook extension, add logic to create the item in Lightspeed first
    • Take whatever ID you get back from Lightspeed and pass that as the primary key for your Directus record

This approach ensures your Directus records always have the corresponding Lightspeed ID, and you maintain data consistency between both systems.

Check out the hooks documentation to get started building your custom extension.

Here’s an example of a custom hook where I use a filter on the items.create event to generate a public_id to use in urls.

import { defineHook } from '@directus/extensions-sdk';
import { nanoid } from 'nanoid';

const COLLECTIONS_WITH_PUBLIC_ID = ['organizations', 'people', 'forms'];

export default defineHook(({ filter, action }) => {
    // Add a public nanoid for use in public URLs
	filter('items.create', (payload: any, schema: any, accountability: any) => {
        if (COLLECTIONS_WITH_PUBLIC_ID.includes(schema.collection) && !payload.public_id) {
            payload.public_id = nanoid(12); // 12 char to limit potential for collisions
        }
        return payload;
	});
});

Thanks for the tips. Much appreciated!