Build type-safe Nuxt 4 composables with @directus/sdk and Clerk Auth. Invoke when integrating Directus data, queries, assets, and analytics.
composables/ with useDirectus* namingSchema matching Directus collectionsuseAsyncData for SSR-friendly fetching and cachinguseDirectusUrl for all asset transformations (width, height, quality)Create a global Schema type representing Directus collections and fields.
// types/directus.ts
export type Schema = {
articles: {
id: string
title: string
slug: string
image: string
status: string
created_at: string
updated_at: string
category: string | null
author: string | null
}
}
Create a client that supports both static tokens for SSR and dynamic Clerk tokens for user actions.
// composables/useDirectusClient.ts
import { createDirectus, rest, staticToken } from '@directus/sdk'
import type { Schema } from '~/types/directus'
export function useDirectusClient(userToken?: string) {
const config = useRuntimeConfig()
const base = String(config.public.directusUrl || '')
const client = createDirectus<Schema>(base).with(rest())
if (userToken) {
return client.with(staticToken(userToken))
}
const token = String(config.public.directusToken || '')
if (token) client.with(staticToken(token))
return client
}
Typed list retrieval with filtering, searching, field selection, sort, pagination.
// composables/useDirectusItems.ts
import { readItems } from '@directus/sdk'
export function useDirectusItems<C extends keyof Schema>(
collection: C,
params?: {
filter?: Record<string, unknown>
search?: string
fields?: string[]
sort?: string[] | string
page?: number
limit?: number
},
opts?: any
) {
const client = useDirectusClient()
const key = `items:${String(collection)}:${JSON.stringify(params || {})}`
return useAsyncData(key, () => client.request(readItems(String(collection), params as any)), opts)
}
Fetch analytics and metrics for Chart.js.
// composables/useDirectusAggregate.ts
import { readAggregation } from '@directus/sdk'
export function useDirectusAggregate<C extends keyof Schema>(
collection: C,
params: { aggregate: Record<string, string[]>, groupBy?: string[], filter?: any }
) {
const client = useDirectusClient()
const key = `agg:${String(collection)}:${JSON.stringify(params)}`
return useAsyncData(key, () => client.request(readAggregation(String(collection), params as any)))
}
Helper to generate optimized image URLs using Directus (Sharp).
// composables/useDirectusAssets.ts
export function useDirectusUrl(fileId: string, options?: { width?: number, height?: number, quality?: number }) {
const config = useRuntimeConfig()
if (!fileId) return ''
let url = `${config.public.directusUrl}/assets/${fileId}`
if (options) {
const params = new URLSearchParams(options as any)
url += `?${params.toString()}`
}
return url
}
Typed create, update, delete helpers using Clerk identity.
// composables/useDirectusMutations.ts
import { createItem, updateItem, deleteItem } from '@directus/sdk'
export async function useDirectusSubmit<C extends keyof Schema>(collection: C, payload: Partial<Schema[C]>) {
const { getToken } = useAuth()
const token = await getToken({ template: 'directus' })
const client = useDirectusClient(token || undefined)
return client.request(createItem(String(collection), payload as any))
}
useAsyncData keys derived from collection and paramsuseDirectusUrl for optimizationuseDirectusAggregateuseAuth from Clerk to obtain a JWT// Fetching with search
const articles = await useDirectusItems('articles', { search: 'nuxt', limit: 5 })
// Generating an optimized image URL
const thumb = useDirectusUrl(article.image, { width: 200, height: 200, quality: 80 })
// Aggregating for a Chart.js component
const { data: stats } = await useDirectusAggregate('articles', {
aggregate: { count: ['*'] },
groupBy: ['category']
})