Accessing Directus from the Frontend
Directus is a headless CMS, so you use it by dynamically fetching content from the frontend, or by fetching content at build time with a tool such as SSG. The official SDK is essential for that.
The official SDK is described in detail on the following page, and it provides fairly solid support.
Setup is as simple as adding the package to your project.
$ npm install @directus/sdk
By the way, if you want to access information in Directus, be careful to properly configure read permissions. I think the permission settings screen is fairly easy to understand.

Directus provides two broad interfaces for access: REST and GraphQL. There is also a more ambitious interface called Realtime, but I am saving that for later.
Usually, accessing CMS information means using either REST or GraphQL. In my own project, I prepare and use both clients as shown below. NEXT_PUBLIC_DIRECTUS_URL is the URL where the CMS is hosted, and it is loaded from an environment variable.
import { createDirectus, graphql, rest } from "@directus/sdk";
// Client with GraphQL support
export const graphqlClient = createDirectus(process.env.NEXT_PUBLIC_DIRECTUS_URL || '').with(graphql());
// Client with Rest support
export const restClient = createDirectus(process.env.NEXT_PUBLIC_DIRECTUS_URL || '').with(rest());
So what kind of difference is there between accessing data with the REST version and the GraphQL version?
Let us try loading a typical page. This page is built so that multilingual content can be registered. To retrieve it, you need to retrieve translated content nested one level down, so the specification becomes a little complex.
First, the GraphQL version. Use graphqlClient and specify a query. The reason for writing / graphql / is that it turns on syntax highlighting inside the template literal. This function only wants one specific page, so it filters by slug, which is something like a page ID.
export async function getPage(
slug: string,
): Promise<Pages | null> {
const result = await graphqlClient.query(/* graphql */ `query {
pages(filter: {slug: {_eq: "${slug}"}}) {
id
status
slug
translations {
id
title
body
languages_code {
code
name
direction
}
}
}
}`);
return result?.pages || null;
}
Next, the REST version. The REST version has a different detailed API, but it also filters with an object and specifies the necessary fields.
export async function getPage(
slug: string,
): Promise<Pages | null> {
const result = await restClient.request<Articles[]>(
readItems('pages', {
filter: {
slug: {
_eq: slug,
},
},
fields: [
'*',
{
translations: ['*'],
},
],
})
);
// console.log(result);
return result || null;
}
After using both, my current conclusion is that the REST version is easier to use. That is because specifying things with objects makes it easier to add various kinds of processing in code.
In addition, Directus's GraphQL specification has some quirks, and there were several times when I had to use trial and error to figure out how to extract what I wanted. I found myself wishing there were a bit more tooling for building queries.