npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@onderwijsin/nuxt-directus-client

v0.12.0

Published

Server-safe Directus REST integration for Nuxt applications.

Readme

@onderwijsin/nuxt-directus-client

Typed, server-safe Directus REST access for Nuxt 4. Use it for browser and SSR requests, preview lookups, generated schema types, normalized errors, and optional cookie-backed authentication.

Install

pnpm add @onderwijsin/nuxt-directus-client @onderwijsin/nuxt-directus-config
// nuxt.config.ts
export default defineNuxtConfig({
  modules: ["@onderwijsin/nuxt-directus-config", "@onderwijsin/nuxt-directus-client"]
});
// directus.config.ts
import { defineDirectusConfig } from "@onderwijsin/nuxt-directus-config/config";

export default defineDirectusConfig({
  instance: {
    baseUrl: process.env.DIRECTUS_URL,
    proxyToken: process.env.DIRECTUS_PROXY_TOKEN
  },
  client: {
    commands: ["readItem", "readItems"]
  }
});

@onderwijsin/nuxt-directus-config is optional. Without it, configure the same shape directly:

// nuxt.config.ts
export default defineNuxtConfig({
  modules: ["@onderwijsin/nuxt-directus-client"],
  directusClient: {
    instance: {
      baseUrl: process.env.DIRECTUS_URL,
      proxyToken: process.env.DIRECTUS_PROXY_TOKEN
    },
    client: {
      commands: ["readItem", "readItems"]
    }
  }
});

Direct module options and directus.config.ts use the same instance and client shape. When both are present, direct module options take precedence. instance.baseUrl and instance.proxyToken are server-only. The proxy token is server-held but delegated through the public application proxy, so its permissions must be safe for public application callers to exercise; secrecy does not make those permissions private. Never put Directus credentials in runtimeConfig.public or browser code.

instance.baseUrl is optional. The module skips setup during nuxt prepare and CI when it is not configured; any request made without it fails with a clear runtime error. Set directusClient.enabled: false when an application does not configure Directus.

Quick start

The default auto-imports are readItem and readItems from @directus/sdk together with the module's composables:

const articles = await useDirectus(readItems("articles", { limit: 10 }));

Browser requests use the same-origin proxy. SSR requests use Directus directly. Credentials are selected on the server, so browser callers cannot override the configured credential.

For Nitro handlers, use the server composable:

export default defineEventHandler((event) =>
  useDirectusServer(readItems("articles", { limit: 10 }), event)
);

When importing the helper from reusable server-side module code, use the published server runtime entrypoint:

import {
  useDirectusServer,
  useDirectusServerItemByPath
} from "@onderwijsin/nuxt-directus-client/runtime/server";

Composables

All composables below are auto-imported. Import commands that are not configured in directusClient.client.commands directly from @directus/sdk.

useDirectus

useDirectus<Output>(command: RestCommand<Output, Schema>): Promise<Output>

Runs a Directus REST command in Vue code. In the browser, it uses the configured same-origin proxy; during SSR, it talks to Directus directly. The module selects the server-side credential, so callers cannot supply one through request headers.

useDirectusServer

useDirectusServer<Output>(command: RestCommand<Output, Schema>, event?: H3Event): Promise<Output>

Runs a command from Nitro. Pass the current event to apply its preview context and, when authentication is enabled, its session.

useDirectusServerAuth

useDirectusServerAuth(event: H3Event): Promise<DirectusSessionSnapshot | null>

Resolves the current token-free Directus session snapshot using the request-scoped, refresh-aware authentication boundary. It returns null when the request is unauthenticated, the sealed session is invalid, or authentication is disabled. Transient refresh failures propagate to the caller; this helper does not silently fall back to stale local state. Access and refresh tokens are never returned.

export default defineEventHandler(async (event) => {
  const session = await useDirectusServerAuth(event);

  if (!session) {
    throw createError({ statusCode: 401 });
  }

  return { userId: session.userId };
});

useDirectusItemByPath

useDirectusItemByPath(collection, query): Promise<Item | null>

Returns the first item matching a Directus query, or null. It is intended for route lookups such as a page by slug and automatically applies the current route's preview context.

useDirectusServerItemByPath

useDirectusServerItemByPath(event, collection, query): Promise<Item | null>

The Nitro equivalent of useDirectusItemByPath. Use it from server handlers when the lookup must read preview values from the request event.

useDirectusError

useDirectusError(error: unknown): DirectusErrorResult

Normalizes Directus, SDK, ofetch, H3, and malformed errors. The result has isDirectusError, isNitroError, a safe errors list, an optional statusCode, and flags for OTP, invalid credentials, forbidden, expired or invalid tokens, validation, rate-limit, service-unavailable, and route-not-found errors. Local authentication validation failures are marked with isNitroError and expose isInvalidAuthInput, isInvalidEmailInput, isInvalidPasswordInput, isInvalidOtpInput, isInvalidPasswordResetTokenInput, and isInvalidMagicLinkTokenInput. Their errors entries use Nitro codes such as INVALID_PASSWORD_INPUT and preserve safe validation details including the field, message, and maximum length. Unknown errors return both discriminator flags as false with an empty errors list.

useDirectusAuth

useDirectusAuth is available only when client.auth.enabled is true. Its full state and method reference is in Authentication.

Configuration

All options are configured under directusClient:

| Option | Default | Contract | | ------------------------------------- | ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | | enabled | true | Enables the module. | | instance.baseUrl | — | Optional Directus URL. Required before requests can run. | | instance.proxyToken | — | Server-held credential delegated through the proxy; its permissions must be safe for public callers. | | client.proxy.path | /_directus/proxy | Absolute local same-origin browser proxy path. Root paths, auth-route collisions, and overlaps with client.assets.path are rejected. | | client.assets.enabled | true | Registers the dedicated Directus /assets proxy when enabled. | | client.assets.url | — | Optional absolute upstream asset base URL; defaults to instance.baseUrl with /assets. | | client.assets.path | /_directus/assets | Absolute local asset-proxy path; uses the same safe local-path validation and cannot overlap the REST proxy or reserved auth routes. | | client.assets.publicOnly | false | Uses anonymous Directus asset requests only; session authentication is never attempted when enabled. | | client.assets.cache.enabled | false | Enables server-side caching for explicitly public anonymous asset responses. | | client.assets.cache.storage | — | Nitro storage mount name, required when enabled; it must support raw binary values. | | client.assets.cache.maxAge | — | Positive fresh cache lifetime in seconds, required when enabled. | | client.assets.cache.maxBodySize | 10485760 | Maximum response size in bytes that may be buffered for caching. | | client.assets.cache.swr | false | Enables stale-while-revalidate behavior. | | client.assets.cache.staleMaxAge | — | Optional non-negative stale lifetime in seconds. | | client.assets.cache.prune.enabled | false | Opts into pruning expired entries when storage does not enforce physical TTLs. | | client.assets.cache.prune.onRequest | true | Enables throttled background pruning after cached asset requests. | | client.assets.cache.prune.interval | 3600 | Minimum interval between request-triggered prune attempts, in seconds. | | client.commands | [readItem, readItems] | SDK commands to auto-import. Unsupported names are rejected. | | client.preview.enabled | false | Enables preview query parsing and request-scoped preview credentials; set to true to opt in. | | client.preview.versioning | true | Enables versioned preview lookup. | | client.preview.queryKeys | preview, token, version, id | Query parameter names used for preview context. | | client.auth.enabled | false | Enables cookie authentication, authentication routes, and useDirectusAuth. | | client.auth.turnstile.enabled | false | Registers Turnstile and protects login plus password-reset-email requests. | | client.auth.magicLinks.enabled | false | Registers optional magic-link request and redemption routes; requires auth to be enabled. | | client.auth.magicLinks.redirectUrl | — | Fixed absolute callback URL sent upstream; required when enabled and server-only. | | client.auth.cookie.name | directus_session | Session cookie name. | | client.auth.cookie.secure | true | Sends the cookie only over HTTPS. Use false only for local HTTP development. | | client.auth.cookie.sameSite | lax | Cookie SameSite policy. | | client.auth.cookie.path | / | Cookie path. | | client.auth.cookie.maxAge | 2592000 | Cookie lifetime in seconds. | | client.auth.cookie.domain | — | Optional cookie domain. | | client.auth.refreshSafetyWindow | 30000 | Refreshes a session this many milliseconds before expiry. | | client.auth.sessionSecret | — | Server-only H3 sealing secret; required when auth is enabled and must contain at least 32 characters. | | client.auth.previousSessionSecrets | [] | Server-only previous sealing secrets tried during key rotation, in order. | | client.auth.maskSecretsInPlayground | true | Masks tokens in the local sealed-session playground inspection page. | | client.auth.passwordResetUrl | — | Required for password-request support; sent to Directus as reset_url. | | client.auth.user.enabled | false | Enables the opt-in current-user fetch; requires authentication. | | client.auth.user.fields | — | Required non-empty recursive Directus QueryFields selection when enabled. | | client.auth.user.mapper | — | Server-only synchronous mapper from executable shared config; not accepted in raw Nuxt options. | | client.typegen.enabled | true | Enables generated #directus declarations. | | client.typegen.introspectionToken | — | Server-only Directus schema introspection token. | | client.typegen.cache.maxAge | 3600000 | Development type-generation cache lifetime in milliseconds. | | client.typegen.augmentations | all true | Optional generated-output transforms. | | client.typegen.rules | {} | Generated field type overrides keyed by collection and field. | | client.typegen.transform | — | Final build-time source transform. |

The module validates options during Nuxt configuration. Production and CI type generation require both instance.baseUrl and client.typegen.introspectionToken when it is enabled.

The asset proxy normalizes upstream Vary headers to Accept, matching the request representation that the proxy exposes and the optional asset cache keys. It does not vary on Origin, request Cache-Control, or Accept-Encoding.

Asset delivery uses a streaming proxy when caching is disabled. When caching is enabled, only anonymous public responses participate in the application-scoped asset cache. If an asset requires the current session, its response is marked Cache-Control: private, no-store for downstream clients.

Pruning is opt-in for storage backends that do not reliably expire entries. Request-triggered pruning runs in the background and is throttled by client.assets.cache.prune.interval; it never blocks asset delivery. The package also exports an optional Nitro task for consumers to register manually:

client: {
  assets: {
    cache: {
      prune: { enabled: false, onRequest: true, interval: 3600 }
    }
  }
}
// server/tasks/directus-assets/prune.ts
export { default } from "@onderwijsin/nuxt-directus-client/runtime/prune-task";

Enable Nitro's experimental tasks and optionally schedule directus-assets:prune in the consumer application. The module does not enable task infrastructure or add a schedule automatically:

export default defineNuxtConfig({
  nitro: {
    experimental: { tasks: true },
    scheduledTasks: { "0 * * * *": ["directus-assets:prune"] }
  }
});

Version previews

Directus Content Versions are independent, unpublished changes to a main item. A version has a key—the value Directus uses in the REST version query parameter—and the main item remains the canonical item. When the preview URL contains preview=true, an item id, and a version key, useDirectusItemByPath and useDirectusServerItemByPath read that item with the selected version. Without a valid version context, they retain their normal first-matching-item lookup behavior.

Configure Directus before relying on version previews:

  1. In Settings → Data Model, enable Content Versioning for each previewed collection and create versions in the item editor.
  2. Set the collection's Preview URL to your application route. Insert the item ID and Version dynamic values so it supplies the module's default query names, for example https://app.example.test/pages/{{slug}}?preview=true&id={{id}}&version={{version}}.
  3. Previewing unpublished content also needs a credential that can read it. We recommend Tokenized Preview Endpoint for Directus: install it in Directus, configure its preview base URL, and prefix the collection Preview URL with its /preview/ endpoint. It appends a short-lived token to the application URL. Keep its TOKENIZED_PREVIEW_TOKEN_KEY as token, or set client.preview.queryKeys.token to match.

For example, an extension-backed URL can be configured as:

// Directus collection Preview URL (use Directus' dynamic-value picker for the placeholders)
/preview/https://app.example.test/pages/{{slug}}?preview=true&id={{id}}&version={{version}}

Preview handling is disabled by default. Set client.preview.enabled to true to opt in. The default preview query keys are preview, token, version, and id; they can be renamed with client.preview.queryKeys. Tokens stay request-scoped and are never exposed through public runtime configuration. Set client.preview.enabled to false to ignore all preview parameters, or client.preview.versioning to false to ignore only the version. This section covers credentialed lookup, not the embedded editor; see Live preview and framing for that setup.

Authentication

Enable authentication with directusClient.client.auth.enabled: true:

const auth = useDirectusAuth();

await auth.login({
  email: "[email protected]",
  password: "password",
  otp: "123456"
});

if (auth.isAuthenticated.value) {
  console.log(auth.userId.value);
}

The session snapshot is persisted with the access and rotating refresh token in a bounded sealed httpOnly cookie. The snapshot contains only stable authentication facts (userId and requiresTfaSetup); mutable profile data is never stored in the cookie. SSR refreshes an expiring access token when possible before projecting the snapshot into Nuxt state, so hydration does not require a session fetch. If refresh is temporarily unavailable, SSR falls back to the trusted local snapshot; terminal authentication failures still clear the session. Access and refresh tokens never enter client state or application code. H3 authenticated encryption protects the cookie's confidentiality and integrity; Directus remains the authorization boundary.

Authentication mutations use Nuxt's request-aware fetch against the same-origin /_directus/auth/ endpoints. The composable remains SSR-safe: reading isAuthenticated, userId, and the session state works during SSR. Automatic SSR session refresh happens directly through the Nitro request boundary, not through an internal HTTP refresh call. Authenticated upstream requests remain strict: they do not send an expired or unusable credential when refresh is temporarily unavailable.

The authentication boundaries have distinct responsibilities: getDirectusSessionSnapshot(event) reads trusted local session state without refreshing and is an internal server primitive; useDirectusServerAuth(event) represents current server authentication state and is refresh-aware; and directusAuth.resolve() resolves request-scoped usable credentials and authentication state. SSR normally uses refresh-aware resolution, but falls back to getDirectusSessionSnapshot(event) only for an explicitly classified transient refresh failure.

Mutations that do not depend on writing a new browser cookie can work naturally through the internal route. Login, refresh, logout, and magic-link redemption may require response-cookie propagation when invoked during SSR because Set-Cookie from a nested internal request is not automatically equivalent to writing on the outer SSR response. Initial SSR POST mutations may also be subject to the same-origin and CSRF requirements described below. Do not rely on SSR mutation calls for establishing or clearing a browser session unless the outer response explicitly propagates the cookie.

When authentication is enabled, configure client.auth.sessionSecret from a cryptographically random, server-only value of at least 32 characters. Existing unsigned cookies are rejected and cleared. To rotate a secret without signing everyone out, put the old value in previousSessionSecrets; new or read sessions are sealed with the active secret and old values are removed after the migration overlap period. H3's derived session header is disabled for this module (sessionHeader: false), so the Directus session is accepted only from the configured cookie. Generate a session secret with:

openssl rand -base64 32

During local development, the module supplies a fixed convenience secret when authentication is enabled without an explicit value. nuxt prepare and CI use a fresh cryptographically random ephemeral secret so generated or tested artifacts never inherit the development value. Production does not provide a fallback: configure an explicit deployment secret to keep sessions stable across builds and deployments.

useDirectusAuth API

The composable exposes a token-free, reactive session snapshot:

| State | Type | Contract | | ------------------------ | ---------------------------------------------------- | --------------------------------------------------------------------- | | auth._session | DeepReadonly<Ref<DirectusSessionSnapshot \| null>> | Read-only snapshot containing identity fields and requiresTfaSetup. | | auth.isAuthenticated | DeepReadonly<ComputedRef<boolean>> | true when a snapshot exists. | | auth.userId | DeepReadonly<ComputedRef<string \| undefined>> | Current user ID. | | auth.magicLinksEnabled | boolean | Whether the magic-link facade is enabled. | | auth.requiresTfaSetup | DeepReadonly<ComputedRef<boolean>> | Server-derived informational TFA setup requirement. |

The snapshot contains userId and requiresTfaSetup, which reflects Directus' enforce_tfa claim. It deliberately contains no profile fields, access or refresh token, role, policy, or permission helpers. requiresTfaSetup is informational; the consuming application owns any TFA setup UX or navigation.

| Method | Signature | Behavior | | ------------------ | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | login | login({ email, password, otp? }, meta?): Promise<void> | Authenticates, fetches only the stable user ID for the session, writes the session cookie, updates state, and emits directus:auth:login. | | refresh | refresh(): Promise<void> | Refreshes and rotates the token pair without refetching profile data, updates state, and emits directus:auth:refresh. Terminal session/auth rejection clears state and emits directus:auth:invalidated; transient refresh availability failures preserve state and rethrow. | | logout | logout(): Promise<void> | Attempts upstream logout, always clears local state and cookie, and emits directus:auth:logout. An upstream failure is rethrown after cleanup. | | passwordRequest | passwordRequest(email, meta?): Promise<void> | Requests a password-reset email using client.auth.passwordResetUrl. | | passwordReset | passwordReset(token, password): Promise<void> | Completes a Directus password reset. | | requestMagicLink | requestMagicLink(email, meta?): Promise<void> | Requests a passwordless login link when magic links are enabled. | | redeemMagicLink | redeemMagicLink(token, otp?): Promise<void> | Redeems a token, establishes the normal session, and emits directus:auth:login. |

meta may be { turnstileToken?: string } when Turnstile protection is enabled.

Current user

Current-user data is opt-in and separate from authentication:

export default defineDirectusConfig({
  client: {
    auth: {
      enabled: true,
      user: {
        enabled: true,
        fields: ["id", "email", "first_name", "last_name", { role: ["id", "name"] }]
      }
    }
  }
});

fields is required when enabled, must be non-empty, and supports nested Directus QueryFields. The default is { enabled: false }; the module never expands it to * or adds id implicitly.

Use useDirectusUser() for mutable profile/application data:

const { user, status, error, refresh } = useDirectusUser();
await useDirectus(updateMe(payload));
await refresh();

The composable shares the stable directus:user async-data key and uses GET /_directus/auth/user in both browser and SSR. The route has private, no-store semantics and preserves rotated session cookies during SSR. Unauthenticated state is user === null without a fabricated 401. Login refreshes existing user state, logout and invalidation clear it, and token refresh does not refetch it. The user ref is generated from the configured fields. With automated type generation it uses the generated DirectusUser, including custom system-collection fields; when type generation is disabled it falls back to the SDK user type. An executable mapper replaces either selection with its inferred return type.

An executable directus.config.ts may add a synchronous server-only mapper that returns a plain object. Its parameter exposes selected SDK user fields as optional values, while custom fields remain unknown until narrowed. Register @onderwijsin/nuxt-directus-config when using a mapper. Profile mutations require an explicit refresh() when immediate local freshness is needed.

Magic links

Install the directus-magic-links-bundle extension in Directus, then enable magic links with a fixed callback URL:

export default defineNuxtConfig({
  directusClient: {
    client: {
      auth: {
        enabled: true,
        magicLinks: {
          enabled: true,
          redirectUrl: "https://app.example.test/auth/magic-link"
        }
      }
    }
  }
});

Request and redeem links through the existing authentication facade:

const auth = useDirectusAuth();

await auth.requestMagicLink("[email protected]");
await auth.redeemMagicLink(token, otp);

The configured callback URL is fixed server configuration; the browser cannot override it. Directus access and refresh tokens remain in the sealed HTTP-only session. auth.requiresTfaSetup exposes the server-derived enforce_tfa state as informational data. The consuming application owns the callback page, URL token extraction and cleanup, OTP UI, TFA setup navigation, return-to state, and post-login navigation. When magic links are disabled, these facade methods perform no network request.

Directus MFA failures are exposed through useDirectusError(error).isOtpError, allowing the UI to ask for an OTP and retry auth.login or auth.redeemMagicLink. Local redemption token validation uses INVALID_MAGIC_LINK_TOKEN_INPUT and isInvalidMagicLinkTokenInput.

When client.auth.enabled is false, Directus session cookies are ignored: they are not read, refreshed, forwarded upstream, or added to the SSR payload. Proxy, preview, and unauthenticated access remain available.

Authentication routes are registered under /_directus/auth/: login, refresh, logout, session, password-request, and password-reset. SSR authentication bootstrap, the session route, and authenticated Directus requests refresh when the access token enters the configured safety window. They can also refresh an access token that has already expired; Directus decides whether the refresh token is still valid. The refresh request is attempted exactly once because Directus may rotate the refresh token even when a response is lost. The existing safe user snapshot is reused after rotation; only access-token-derived state such as requiresTfaSetup is recalculated. If a later local persistence step fails, the old session is cleared because its refresh token may already be invalid. Refresh coordination uses the directus-auth-refresh Nitro mount: the memory driver provides process-local single-flight coordination, while the redis driver provides atomic cross-process coordination using its configured Redis client. Configure Redis for multiple Node processes, containers, replicas, or Cloudflare isolates; unsupported drivers fail explicitly. Refresh results are H3-sealed and short-lived, and the configured Redis backend must be treated as sensitive infrastructure. Completed results are reusable for thirty seconds, terminal results for five seconds, and transient failures for only one second to suppress an immediate request burst. This bounded reuse window protects overlapping and near-concurrent refreshes; it is not permanent consumed-token tracking.

When client.auth.magicLinks.enabled is true, the module additionally registers POST /_directus/auth/magic-links/request and POST /_directus/auth/magic-links/redeem. These routes require the Directus magic-links extension. The request route uses the fixed server-side callback URL and redemption always requests Directus mode: "json", establishing the normal sealed session.

Rotating the session secret

Rotate client.auth.sessionSecret in two deployments: first set the new secret as active while keeping the old secret in client.auth.previousSessionSecrets, then remove the old secret only after all old cookies could have expired. Keep the overlap at least as long as client.auth.cookie.maxAge plus a short deployment window so rolling instances can read and reseal existing sessions. Removing a previous secret invalidates cookies still sealed with it. Keep the old secret available during the overlap on every instance, and treat the shared Nitro storage mount as sensitive while sealed refresh results from the previous deployment can remain within its short TTL.

The login and password-request routes accept emails up to 1024 characters. Login passwords and password-reset passwords may be up to 512 characters, login OTPs up to 6 characters, and password-reset tokens up to 1024 characters. Oversized values are rejected before they are forwarded to Directus.

Authentication mutations require an Origin or Referer matching the application origin. Missing or cross-origin metadata is rejected with 403, including when the session cookie uses sameSite: "none".

The sealed-session inspection endpoint belongs only to the local playground and returns 404 in production builds. Keep the playground out of production deployments and leave secret masking enabled unless a local diagnostic explicitly requires otherwise.

Turnstile protection

Set client.auth.turnstile.enabled: true to register @onderwijsin/nuxt-turnstile and require a Turnstile token for login, password-reset-email, and magic-link request operations. Configure the Turnstile site and secret keys through the usual top-level turnstile option. Follow the Turnstile module guide to configure keys, render the widget, and manage the token lifecycle. The Directus module exposes the required widget actions through public runtime config, and the optional second argument to each auth method forwards the token in x-turnstile-token:

const config = useRuntimeConfig();
const auth = useDirectusAuth();
const token = await getTokenWithRetry();

await auth.login({ email: "[email protected]", password: "password" }, { turnstileToken: token });

const passwordRequestAction = config.public.directusClient.auth.turnstile.actions.passwordRequest;

Use config.public.directusClient.auth.turnstile.actions.login for the login widget and passwordRequest for the password-reset-request widget and magicLinkRequest for the magic-link request widget. Cloudflare's test credentials return a verified test-key response without an action, which the module recognizes only for those credentials. Tokens are required only when the option is enabled; reset each widget after its submission because Turnstile tokens are single-use.

Generated types

When type generation is enabled, the module uses directus-sdk-typegen to inspect your Directus schema and expose collection interfaces plus Schema from the virtual #directus module:

import type { Article, Schema } from "#directus";

Configure it under directusClient.client.typegen:

| Option | Use it when | | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | enabled | Set to false to skip generation. An existing declaration is reused when available; otherwise Schema is empty. | | introspectionToken | Supply a server-only token with enough Directus permissions to inspect the schema. It is required for enabled generation outside local development. | | cache.maxAge | Adjust the development-only cache lifetime (milliseconds). CI and production always regenerate. | | augmentations | Individually disable a generated-output transform when its default normalization does not fit your schema. All default to true. | | rules | Replace a generated field type deterministically, keyed by collection then field: { articles: { body: "RichText" } }. Values must be a single TypeScript type expression. | | transform | Apply a final build-time function to the generated source. It receives the source and metadata including the Directus URL, generator version, collection names, and rules, and must return source text. |

| Augmentation | Effect | | ---------------------------------- | ------------------------------------------------------------------------------ | | removeEnums | Removes generated export enum declarations. | | replaceAnyWithUnknown | Rewrites generated Record<…, any> values to Record<…, unknown>. | | replaceJsonWithJSON | Rewrites quoted "json" field types to JSON. | | makeNonNullableOptionalsRequired | Makes simple optional fields required when their type does not include null. | | mergeJsDocs | Merges adjacent generated JSDoc tag blocks and removes duplicate tags. |

They run in the shown order before rules, followed by transform. Missing or incomplete credentials reuse the previous declaration—or produce an empty Schema—only in local development; CI and production fail clearly instead.

Proxy endpoint

The browser endpoint at proxy.path (default /_directus/proxy) forwards REST requests to the configured Directus instance. This lets browser code use useDirectus without learning the Directus URL or receiving a proxy, preview, or session token. The server chooses credentials in this order: current session when authentication is enabled, preview token, proxy token, then no credential. Preview/version selection is independent from credential selection, so a preview URL does not replace an authenticated session credential.

The proxy preserves the request method, body, query string, response status, and safe response headers. It forwards only REST headers needed for representation, caching, conditional requests, range, and preference semantics. Caller-supplied credential, cookie, host, origin, Referer, connection, hop-by-hop, forwarding, client-IP, and platform identity headers are not forwarded; the preview token is stripped from the upstream query; and upstream Set-Cookie and Access-Control-* headers are never returned to the browser. Upstream non-2xx status codes and bodies are preserved, while network failures remain proxy errors. Credentialed mutations (POST, PUT, PATCH, and DELETE) require a same-origin Origin or Referer header.

It is not a general-purpose proxy, a CORS bypass, a session-token API, or an authorization layer. It only targets the configured Directus URL, and Directus permissions remain the final access control.

Live preview and framing

Configure Directus Live Preview to open the application URL with the module's preview query parameters. The application must allow Directus in its frame-src policy, and the application must be allowed by its own frame-ancestors policy. A version placeholder should be included in the configured preview URL when versioned content is required. The module does not refresh pages automatically; the application decides how to react to iframe updates.

Troubleshooting

  • A browser request failing with a Directus permission error is expected when the selected session, proxy token, or unauthenticated role lacks access. The proxy is not an authorization layer.
  • A missing generated type in production usually means DIRECTUS_URL or DIRECTUS_INTROSPECTION_TOKEN was not available during nuxt prepare/build.
  • A local auth cookie normally needs client.auth.cookie.secure: false when the playground is served over plain HTTP. Keep the secure default in deployed environments.
  • A preview lookup returns null when the application path filter matches no item; preview mode does not change lookup semantics or turn an item path into a Directus primary key.

Security and compatibility

  • Directus URLs, credentials, and session tokens are server-only.
  • Browser requests cross the same-origin proxy, which strips caller-supplied credential and origin headers.
  • State-changing proxy requests that use a server credential require a matching Origin or Referer header. Cross-origin or headerless mutations are rejected, including when sameSite: "none" is configured.
  • Authentication POST routes apply the same origin validation before reading input or changing a session.
  • Directus permissions remain the final authorization boundary.
  • Supported environments are Nuxt 4 and Node.js 24 or newer. Node.js 22 may work but is untested and unsupported.