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-turnstile

v0.3.3

Published

Nuxt Turnstile integration with client helpers and server-side validation.

Readme

@onderwijsin/nuxt-turnstile

Nuxt 4 integration for action-aware Cloudflare Turnstile protection. The module registers @nuxtjs/turnstile and @nuxt/ui, exposes the auto-imported useTurnstile() composable, and provides server helpers for validating single-use Turnstile tokens before a protected operation.

Installation and configuration

pnpm add @onderwijsin/nuxt-turnstile
export default defineNuxtConfig({
  modules: ["@onderwijsin/nuxt-turnstile"],
  turnstile: {
    siteKey: process.env.TURNSTILE_SITE_KEY ?? "",
    secretKey: process.env.TURNSTILE_SECRET_KEY ?? ""
  }
});

The module's options are:

| Option | Default | Purpose | | ----------------- | ----------------- | ----------------------------------------------------- | | enabled | true | Enables or disables module setup and its dependencies | | siteKey | "" | Public key used by the Turnstile widget | | secretKey | "" | Server-only key used for verification | | adminToken | "" | Optional trusted token that bypasses verification | | adminHeaderName | "x-admin-token" | Header accepted for adminToken |

siteKey is public and is also passed to @nuxtjs/turnstile. Keep secretKey and adminToken private. In production, prefer NUXT_TURNSTILE_SECRET_KEY for the private runtime value. The module's dependency registration also requires the consuming app's normal Nuxt UI stylesheet setup.

Protecting a form

Render NuxtTurnstile, choose a stable action for the operation, wait for a token, and forward it in x-turnstile-token. useTurnstile() is auto-imported and provides token lifecycle helpers plus Nuxt UI toast feedback.

<script setup lang="ts">
import { TURNSTILE_TOKEN_HEADER } from "@onderwijsin/nuxt-turnstile/runtime";

const token = ref<string>();
const widget = useTemplateRef<{ reset: () => void }>("turnstile");
const { getTokenWithRetry, isEnabled, showMissingTokenErrorHint, captureTurnstileError } =
  useTurnstile();

async function onSubmit() {
  const currentToken = await getTokenWithRetry();
  if (isEnabled.value && !currentToken) {
    showMissingTokenErrorHint();
    return;
  }

  try {
    await $fetch("/api/user/session", {
      method: "POST",
      body: { email: "[email protected]" },
      headers: currentToken ? { [TURNSTILE_TOKEN_HEADER]: currentToken } : undefined
    });
  } catch (error) {
    if (!captureTurnstileError(error)) throw error;
  } finally {
    widget.value?.reset();
  }
}
</script>

<template>
  <form @submit.prevent="onSubmit">
    <NuxtTurnstile
      ref="turnstile"
      v-model="token"
      :options="{ action: 'magic-link', appearance: 'interaction-only' }"
    />
    <UButton type="submit" label="Send magic link" />
  </form>
</template>

Use getToken() when the widget is already ready, or getTokenWithRetry() when submission may race with widget initialization. Reset the widget after every processed submission because Turnstile tokens are single-use.

Protecting a server route

Call assertTurnstileToken before application validation, persistence, delivery, or other protected work. The expected action must match the widget action.

import { z } from "zod";
import { assertTurnstileToken } from "@onderwijsin/nuxt-turnstile/runtime";

export default defineEventHandler(async (event) => {
  await assertTurnstileToken(event, "magic-link");
  const body = await readValidatedBody(event, z.object({ email: z.email() }).parse);

  return await sendMagicLink(body.email);
});

The helper reads x-turnstile-token and verifies it directly with Cloudflare's Turnstile siteverify endpoint. It rejects missing, failed, unavailable, or mismatched-action tokens. In development, an absent secret leaves local forms usable; in production, it produces TURNSTILE_SERVER_MISCONFIGURED.

Cloudflare's official test credentials return metadata.result_with_testing_key: true without an action. The helper accepts only that verified test-key response without action matching, so test credentials can exercise protected flows while normal credentials remain action-aware.

Trusted administrator bypass

Configure adminToken for trusted server-to-server or administrative requests. The configured adminHeaderName and Authorization: Bearer <token> are both accepted. This bypass is checked before Turnstile verification; do not expose the token in public runtime config, client code, logs, or application aliases.

Runtime exports

The @onderwijsin/nuxt-turnstile/runtime subpath is the explicit runtime API and does not require loading the Nuxt module entrypoint. It exports:

  • TURNSTILE_TOKEN_HEADER ("x-turnstile-token")
  • assertTurnstileToken
  • createTurnstileError
  • createTurnstileErrorData
  • isErrorWithStatusCode
  • TurnstileErrorCode and TurnstileErrorData types

Stable error codes are TURNSTILE_TOKEN_MISSING, TURNSTILE_VALIDATION_FAILED, TURNSTILE_ACTION_MISMATCH, TURNSTILE_VALIDATION_UNAVAILABLE, and TURNSTILE_SERVER_MISCONFIGURED. Apply authorization and business validation in the consuming route after Turnstile succeeds; the module does not replace the route's existing submit or business logic.

verifyTurnstileToken is also auto-imported for server-only use when the raw Cloudflare verification response is needed. Prefer assertTurnstileToken for protected application routes.

Compatibility

  • Nuxt 4
  • Node.js 24+; Node.js 22 may work but is untested and unsupported.
  • Node and Cloudflare Workers-compatible server runtime
  • No Sentry dependency or telemetry is included

Developed and tested against Node.js 24 and Nuxt 4.5.x. Versions outside the current CI matrix are not continuously tested. Nuxt 3 is not guaranteed.