# JavaScript SDK

The JS SDK provides an intuitive interface for the Directus API from within a JavaScript-powered project (browsers and node.js). The default implementation uses Axios (opens new window) for transport and localStorage for storing state.

# Installation

npm install @directus/sdk

# Usage

import { Directus } from '@directus/sdk';

const directus = new Directus('https://api.example.com/');

NOTE All methods return promises. Make sure to await methods, for example:

import { Directus } from '@directus/sdk';

const directus = new Directus('https://api.example.com/');

// Wait for login to be done...
await directus.auth.login({
	email: 'admin@example.com',
	password: 'password',

// ... before fetching items
const articles = await directus.items('articles').readMany();

	items: articles.data,
	total: articles.meta.total_count,

# Global

# Initialize

import { Directus } from '@directus/sdk';

const directus = new Directus('https://api.example.com/');

# url

The constructor accepts the URL as the first parameter.

# options.auth

The authentication implementation. See Auth for more information.

Defaults to an instance Auth.

# options.storage

The storage implementation. See Storage for more information.

Defaults to an instance of MemoryStorage when in node.js, and LocalStorage when in browsers.


If you plan to use multiple SDK instances at once, keep in mind that they will share the Storage across them, leading to unpredictable behaviors. This scenario might be a case while writing tests.

For example, the SDK instance that executed last the login() method writes the resulting access_token into the Storage and overwrites any prior fetched access_token from any other SDK instance. That might mix up your test scenario by granting false access rights to your previous logged-in users.

Adding prefixes to your Storage instances would solve this error:

import { Directus, MemoryStorage } from '@directus/sdk';
import { randomBytes } from 'crypto';

// ...

const prefix = randomBytes(8).toString('hex');
const storage = new MemoryStorage(prefix);
const url = `http://${host}:${port}`;
const directus = new Directus(url, { storage });

# options.transport

The transport implementation. See Transport for more information.

Defaults to an instance of AxiosTransport.

# Example

const directus = new Directus('http://api.example.com/');

# Get / Set API URL

// Get the API base URL
console.log(directus.transport.url); // => https://api.example.com/

// Set the API base URL
directus.transport.url = 'https://api2.example.com';

# Access to transport/Axios

You can tap into the transport through directus.transport. If you are using the (default) AxiosTransport, you can access axios through directus.transport.axios.

# Intercepting requests and responses

Axios transport offers a wrapper around Axios interceptors to make it easy for you to inject/eject interceptors.

const requestInterceptor = directus.transport.requests.intercept((config) => {
	config.headers['My-Custom-Header'] = 'Header value';
	return config;

// If you don't want the interceptor anymore, remove it
const responseInterceptor = directus.transport.responses.intercept((response) => {
	console.log('Response received', { response });
	return response;

// If you don't want the interceptor anymore, remove it

# Items

You can get an instance of the item handler by providing the collection (and type, in the case of TypeScript) to the items function. The following examples will use the Article type.


// import { Directus, ID } from '@directus/sdk';
const { Directus } = require('@directus/sdk');

const directus = new Directus('https://api.example.com');

const articles = directus.items('articles');


import { Directus, ID } from '@directus/sdk';

// Map your collection structure based on its fields.
type Article = {
	id: ID;
	title: string;
	body: string;
	published: boolean;

// Map your collections to its respective types. The SDK will
// infer its types based on usage later.
type MyBlog = {
	// [collection_name]: typescript_type
	articles: Article;

	// You can also extend Directus collection. The naming has
	// to match a Directus system collection and it will be merged
	// into the system spec.
	directus_users: {
		bio: string;

// Let the SDK know about your collection types.
const directus = new Directus<MyBlog>('https://directus.myblog.com');

// typeof(article) is a partial "Article"
const article = await directus.items('articles').readOne(10);

// Error TS2322: "hello" is not assignable to type "boolean".
// post.published = 'hello';

# Create Single Item

await articles.createOne({
	title: 'My New Article',

# Create Multiple Items

await articles.createMany([
		title: 'My First Article',
		title: 'My Second Article',

# Read All

await articles.readMany();

# Read By Query

await articles.readMany({
	search: 'Directus',
	filter: {
		date_published: {
			_gte: '$NOW',

# Read By Primary Key(s)

await articles.readOne(15);

Supports optional query:

await articles.readOne(15, { fields: ['title'] });

# Update Multiple Items

await articles.updateMany([15, 42], {
	title: 'Both articles now have the same title',

Supports optional query:

await articles.updateMany(
	[15, 42],
		title: 'Both articles now have the same title',
		fields: ['title'],

# Delete

// One
await articles.deleteOne(15);

// Multiple
await articles.deleteMany([15, 42]);

# Activity

# Read All Activity

await directus.activity.readMany();

# Read Activity By Query

await directus.activity.readMany({
	filter: {
		action: {
			_eq: 'create',

# Read Activity By Primary Key(s)

await directus.activity.readOne(15);

Supports optional query:

await directus.activity.readOne(15, { fields: ['action'] });

# Create a Comment

await directus.activity.comments.create({
	collection: 'articles',
	item: 15,
	comment: 'Hello, world!',

# Update a comment

await directus.activity.comments.update(31, {
	comment: 'Howdy, world!',

Note: The passed key is the primary key of the comment

# Delete a comment

await directus.activity.comments.delete(31);

Note: The passed key is the primary key of the comment

# Auth

# Configuration

Directus will accept custom implementations of the IAuth interface. The default implementation Auth can be imported from @directus/sdk. The default implementation will require you to pass the transport and storage implementations. All options are optional.

import { Auth } from '@directus/sdk';

// ...

const sdk = new Directus('url', {
	auth: new Auth(transport, storage, options);
	// ...

# transport

The transport responsible for communicating with Directus backend.

Defaults to an instance of AxiosTransport when not creating Auth youself.

# storage

The storage responsible for storing authentication and sdk state.

When not creating Auth youself, defaults to MemoryStorage in node.js, and LocalStorage in browsers.

# options

# options.mode

Accepts cookie or json.

When in cookie mode, the API will set the refresh token in an httpOnly secure cookie that can't be accessed from client side JavaScript. This is the most secure way to connect to the API from a public front-end website.

When you can't rely on cookies, or need more control over handling the storage of the cookie (like in node.js), use json mode. This will return the refresh token in the "normal" payload. The storage of these tokens are handled by the storage implementation.

Defaults to cookie in browsers, json in node.js.

# options.refresh

See Refresh auth token.

# Get current token

const token = directus.auth.token;

# Login

# With credentials

await directus.auth.login({
	email: 'admin@example.com',
	password: 'd1r3ctu5',

# With static tokens

await directus.auth.static('static_token');

# Refresh auth token

You can set authentication to auto-refresh the token once it's close to expire.

await directus.auth.login(
		email: 'admin@example.com',
		password: 'd1r3ctu5',
		refresh: {
			auto: true,

You can also set how much time before the expiration you want to auto-refresh the token. When not specified, 30 sec is the default time.

await directus.auth.login(
		email: 'admin@example.com',
		password: 'd1r3ctu5',
		refresh: {
			auto: true,
			time: 30000, // refesh the token 30 secs before the expiration

# Refresh Auth Token

You can manually refresh the authentication token. This won't try to refresh the token if it's still valid in the eyes of the SDK.

Also worth mentioning that any concurrent refreshes (trying to refresh while a there's an existing refresh running), will result in only a single refresh hitting the server, and promises resolving/rejecting with the result from the first call. This depends on the implementation of IAuth you're using.

await directus.auth.refresh();

You can force the refresh by passing true in the first parameter.

An optional parameter will accept auto-refresh information.

await directus.auth.refresh(true);

This function can either return the AuthResult in case a refresh was made, false in case SDK thinks it's not needed, or throw an error in case refresh fails.

# Logout

await directus.auth.logout();

# Request a Password Reset

By default, the address defined in PUBLIC_URL on .env file is used for the link to the reset password page sent in the email:

await directus.auth.password.request('admin@example.com');

But a custom address can be passed as second argument:

await directus.auth.password.request(
	'https://myapp.com' // In this case, the link will be https://myapp.com?token=FEE0A...

# Reset a Password

await directus.auth.password.reset('abc.def.ghi', 'n3w-p455w0rd');

Note: The token passed in the first parameter is sent in an email to the user when using request()

# Transport

The transport object abstracts how you communicate with Directus. Transports can be customized to use different HTTP libraries for example.

# Interface

// Simplified version, `import { ITransport } from '@directus/sdk';`
interface ITransport {
	delete(path, data = undefined);
	post(path, data);
	put(path, data);
	patch(path, data);

# AxiosTransport

The default transport used in both browser and node deployments. It supports auto refresh on request.

# Options

AxiosTransport requires a base URL and a storage implementation to work.

const transport = new AxiosTransport('http://example.com', new MemoryStorage(), async () => {
	await sdk.auth.refresh();
await transport.get('/server/info');

# Storage

The storage is used to load and save SDK data.

# LocalStorage

The storage used in environments where Local Storage is supported.

# Options

The LocalStorage implementation accepts a transparent prefix. Use this when you need multiple SDK instances with independent authentication for example.

# MemoryStorage

The storage used when SDK data is ephemeral. For example: only during the lifecycle of the process.

# Options

The MemoryStorage implementation accepts a transparent prefix so you can have multiple instances of the SDK without having clashing keys.

# Collections


Same methods as directus.items("directus_collections").

# Fields


Same methods as directus.items("directus_fields").

# Files


Same methods as directus.items("directus_files").

# Folders


Same methods as directus.items("directus_folders").

# Permissions


Same methods as directus.items("directus_permissions").

# Presets


Same methods as directus.items("directus_presets").

# Relations


Same methods as directus.items("directus_relations").

# Revisions


Same methods as directus.items("directus_revisions").

# Roles


Same methods as directus.items("directus_roles").

# Settings


Same methods as directus.items("directus_settings").

# Server

# Ping the Server

await directus.server.ping();

# Get Server/Project Info

await directus.server.info();

# Users


Same methods as directus.items("directus_users"), and:

# Invite a New User

await directus.users.invites.send('admin@example.com', 'fe38136e-52f7-4622-8498-112b8a32a1e2');

The second parameter is the role of the user

# Accept a User Invite

await directus.users.invites.accept('<accept-token>', 'n3w-p455w0rd');

The provided token is sent to the user's email

# Enable Two-Factor Authentication

await directus.users.tfa.enable('my-password');

# Disable Two-Factor Authentication

await directus.users.tfa.disable('691402');

# Get the Current User

await directus.users.me.read();

Supports optional query:

await directus.users.me.read({
	fields: ['last_access'],

# Update the Current Users

await directus.users.me.update({ first_name: 'Admin' });

Supports optional query:

await directus.users.me.update({ first_name: 'Admin' }, { fields: ['last_access'] });

# Utils

# Get a Random String

await directus.utils.random.string();

Supports an optional length (defaults to 32):

await directus.utils.random.string(50);

# Generate a Hash for a Given Value

await directus.utils.hash.generate('My String');

# Verify if a Hash is Valid

await directus.utils.hash.verify('My String', '$argon2i$v=19$m=4096,t=3,p=1$A5uogJh');

# Sort Items in a Collection

await directus.utils.sort('articles', 15, 42);

This will move item 15 to the position of item 42, and move everything in between one "slot" up.

# Revert to a Previous Revision

await directus.utils.revert(451);

Note: The key passed is the primary key of the revision you'd like to apply.

# TypeScript

If you are using TypeScript, the JS-SDK requires TypeScript 3.8 or newer. TypeScript will also improve the development experience by providing relevant information when manipulating your data. For example, directus.items knows about your collection types if you feed the SDK with enough information in the construction of the SDK instance. This allows for a more detailed IDE suggestions for return types, sorting, and filtering.

type BlogPost = {
	id: ID;
	title: string;

type BlogSettings = {
	display_promotions: boolean;

type MyCollections = {
	posts: BlogPost;
	settings: BlogSettings;

// This is how you feed custom type information to Directus.
const directus = new Directus<MyCollections>('http://url');

// ...

const post = await directus.items('posts').readOne(1);
// typeof(post) is a partial BlogPost object

const settings = await posts.singleton('settings').read();
// typeof(settings) is a partial BlogSettings object

You can also extend the Directus system type information by providing type information for system collections as well.

import { Directus } from '@directus/sdk';

// Custom fields added to Directus user collection.
type UserType = {
	level: number;
	experience: number;

type CustomTypes = {
	This type will be merged with Directus user type.
	It's important that the naming matches a directus
	collection name exactly. Typos won't get caught here
	since SDK will assume it's a custom user collection.
	directus_users: UserType;

const directus = new Directus<CustomTypes>('https://api.example.com');

await directus.auth.login({
	email: 'admin@example.com',
	password: 'password',

const me = await directus.users.me.read();
// typeof me = partial DirectusUser & UserType;

// OK
me.level = 42;

// Error TS2322: Type "string" is not assignable to type "number".
me.experience = 'high';