SDK - readUsers with a custom directus_users field?

I’m just getting starting with Directus and the SDK. I’m migrating data from a legacy system into Directus using Node and the SDK with Typecript. The first thing I want to migrate is the user data. I created my own DirectusSchema:

export interface SimpleDirectusUser {
  id: string;
  first_name?: string | null;
  last_name?: string | null;
  email?: string | null;
  legacy_user_id?: string | null;
}

export interface DirectusSchema {
  directus_users?: SimpleDirectusUser[];
}

I’ve added a legacy_user_id to the internal directus_users collection because I need to be able to track that information.

I’m trying to query Directus with the SDK to check if the user has already been migrated:

    const directusClient = createDirectus<DirectusSchema>(DIRECTUS_URL).with(staticToken(ADMIN_TOKEN)).with(rest());

    ...

    const users = await directusClient.request(
      readUsers({
        filter: {
          email: {
            _eq: email,
          },
        },
        fields: ['id', 'email', 'first_name', 'last_name', 'legacy_user_id'],
      })
    );

However, I get a Typescript error for my legacy_user_id field:

Type ‘“legacy_user_id”’ is not assignable to type ‘“id” | “first_name” | “last_name” | “email” | “password” | “location” | “title” | “description” | “tags” | “avatar” | “language” | “theme” | “tfa_secret” | “status” | “role” | … 15 more … | FunctionFields<…>’.

Is there a way I can include my custom field in the resulting user object? This seems to work fine if I omit my legacy_user_id field. I’ve checked the documentation, github issues, and searched for issues here. Any ideas?

Thanks!

5 Answers

5

Must be something specific in the configuration when using tsc, I’ve recreated your setup in the TypeScript playground and am not getting a type error there :thinking:

import { createDirectus, staticToken, rest, readUsers } from '@directus/sdk';

export interface SimpleDirectusUser {
  legacy_user_id?: string | null;
}

export interface DirectusSchema {
  directus_users: SimpleDirectusUser;
}

const directusClient = createDirectus<DirectusSchema>('DIRECTUS_URL').with(staticToken('ADMIN_TOKEN')).with(rest());

const users = await directusClient.request(
    readUsers({
    filter: {
        email: {
            _eq: 'email',
        },
    },
    fields: ['id', 'email', 'first_name', 'last_name', 'legacy_user_id'],
    })
);

Hello @Chromag

Here’s an example code snippet that shows how to extend the directus_users type with a custom field like legacy_user_id. Hope this helps!

import {
  authentication,
  createDirectus,
  readUsers,
  rest,
  DirectusUser as DirectusUserType
} from '@directus/sdk';
import { Env } from '~/types';

const { API_URL } = process.env as unknown as Env;

interface DirectusUser extends DirectusUserType {
  legacy_user_id?: string | null;
}

interface Schema {
  directus_users: DirectusUser;
}

export const directus = createDirectus<Schema>(API_URL)
  .with(authentication('json', { autoRefresh: false }))
  .with(rest());

const players = await directus.request(
  readUsers({ fields: ['legacy_user_id'] })
);

No need for manually extending the DirectusUsers type (the sdk should handle that) and the type on the Schema is without array [] :smiley:

interface MySchema {
    // regular collections are array types
    collection_a: CollectionA[];
    // singleton collections are singular types
    collection_c: CollectionC;
    // extend the provided DirectusUser type
    directus_users: CustomUser;
}

interface CustomUser {
    custom_field: string;
} 

I tried this as well prior to posting and got the same result. Just to make sure I tried making the change again to test. Here is the updated DirectusSchema based on your example:

import { DirectusUser as DirectusUserType } from '@directus/sdk';

export interface DirectusUser extends DirectusUserType {
  legacy_user_id?: string | null;
}

export interface DirectusSchema {
  directus_users?: DirectusUser;
}

Then I tried something basic as an example like the one you provided:

export const directusClient = createDirectus<DirectusSchema>(DIRECTUS_URL).with(staticToken(ADMIN_TOKEN)).with(rest());

const players = await directusClient.request(readUsers({ fields: ['legacy_user_id'] }));

I still get the same error:

Type ‘“legacy_user_id”’ is not assignable to type …


Gah! I fixed this but I’m still getting the same Typescript error. So the original code I posted should work? (after fixing this to not use an array)

This is strictly a typescript error in vscode (and when using tsc to build). If I ignore this and run the script from the terminal (using tsx) it works as expected and the legacy_user_id is both written and read correctly.

Thanks for all the help and suggestions!

I figured it out! I almost don’t want to admit what the problem ended up being but if it helps anyone else I guess it’s worth admitting. Well, ok, there were a few problems:

  1. Don’t use an array
  2. Based on the documentation you linked (thanks for that!) it specifically says this:

To define custom fields on the core collections, add a type containing only your custom fields as a singular type.

I fixed those two things but i was still getting the same error about my legacy_user_id.

Here is what was causing that error with my custom field:

Your example:

export interface DirectusSchema {
  directus_users: SimpleDirectusUser;
}

My original:

export interface DirectusSchema {
  directus_users?: SimpleDirectusUser;
}

Oh that little ? to make directus_users an optional property. As soon as I removed that the error cleared up.

Thanks so much for your help!