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!

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!