If you're using Directus as your headless CMS and TanStack Start for your frontend, you don't need to write manual fetch calls or build your own auth headers. The Directus SDK handles all of it cleanly.
Here's how I structure a single directus.ts file that covers authentication, typed data fetching, filtering, CRUD operations and file uploads.
Installing the SDK
npm install @directus/sdk
Setting Up the Client
Create the client once and export it. Passing your schema type as a generic is what unlocks end-to-end type safety across every request.
import type { Product, Navigation, CartItem, Order } from '@/types';
import {
authentication,
createDirectus,
rest,
readItems,
createItem,
updateItem,
deleteItem,
readMe,
updateMe,
deleteUser,
uploadFiles,
registerUser as registerUserDirectus,
} from '@directus/sdk';
const directusUrl =
import.meta.env.VITE_DIRECTUS_URL ??
process.env.VITE_DIRECTUS_URL ??
'https://your-directus-url.com';
const directus = createDirectus(directusUrl)
.with(authentication('session', { credentials: 'include' }))
.with(rest({ credentials: 'include' }));
Using authentication('session') with credentials: 'include' means cookies are handled automatically — no manual token management needed.
Fetching Data with Full Type Safety
Each collection gets its own exported async function with a typed return value.
export async function getProducts(): Promise<Product[]> {
const items = await directus.request(readItems('products'));
return items as Product[];
}
Filtering is First-Class
The SDK's filter syntax maps directly to Directus's query engine — no raw query strings, no URL building.
export async function getProductsByCategory(
category: string
): Promise<Product[]> {
const items = await directus.request(
readItems('products', {
filter: { category: { _eq: category } },
})
);
return items as Product[];
}
What Else is Covered
The same client and pattern covers the full CRUD surface:
-
readItems— fetch collections -
createItem— insert records -
updateItem— update records -
deleteItem— delete records -
uploadFiles— handle file uploads -
readMe/updateMe/deleteUser— user profile management -
registerUser— user registration
All importable directly from @directus/sdk, all typed.
Using it in a TanStack Start Loader
TanStack Start's file-based routing and loader pattern pairs perfectly with this setup. Data is fetched server-side and ready before the component renders.
export const Route = createFileRoute('/products')({
loader: () => getProducts()
});
No boilerplate, no custom wrappers, no type assertions. One file, full coverage.
If you're evaluating Directus as a headless CMS for a TanStack Start project this setup gets you up and running quickly with a clean, maintainable data layer from day one.
Top comments (0)