Captcha

Protect your forms and API call with captcha validation.

Support

VitNode supports multiple captcha providers. You can choose the one that fits your needs. Currently, we support:

If you need more providers, feel free to open a Feature Request on our GitHub repository :)

Usage

In this example, we will show you how to use captcha in your forms. We will use the AutoForm component to render the form and handle the captcha validation.

Activate captcha in route

Add withCaptcha to your route config to enable captcha validation for this route.

plugins/{plugin_name}/src/routes/example.ts
import { buildRoute } from '@vitnode/core/api/lib/route';

export const exampleRoute = buildRoute({
  ...CONFIG_PLUGIN,
  route: {
    method: 'post',
    description: 'Create a new user',
    path: '/sign_up',
    withCaptcha: true, 
  },
  handler: async c => {},
});

Get config from middleware API

Get captcha config from middleware API in your view and pass it to your 'use client'; component.

plugins/{plugin_name}/src/app/sing_up/page.tsx
import { getMiddlewareApi } from '@vitnode/core/lib/api/get-middleware-api'; 

export const SignUpView = async () => {
  const { captcha } = await getMiddlewareApi(); 

  return <FormSignUp captcha={captcha} />;
};

Use in form

Get the captcha config from the props and pass it to the AutoForm component. This will render the captcha widget in your form.

plugins/{plugin_name}/src/components/form/sign-up/sign-up.tsx
'use client';

import { AutoForm } from '@vitnode/core/components/form/auto-form';

export const FormSignUp = ({
  captcha, 
}: {
  captcha: z.infer<typeof routeMiddlewareSchema>['captcha']; 
}) => {
  return (
    <AutoForm<typeof formSchema>
      captcha={captcha} 
      fields={[]}
      formSchema={formSchema}
    />
  );
};

AutoForm

Lear more about the AutoForm component and how to use it.

Submit form with captcha

In your form submission handler, you can get the captchaToken from the form submission context and pass it to your mutation API.

plugins/{plugin_name}/src/components/form/sign-up/sign-up.tsx
'use client';

import {
  AutoForm,
  type AutoFormOnSubmit, 
} from '@vitnode/core/components/form/auto-form';

export const FormSignUp = ({
  captcha,
}: {
  captcha: z.infer<typeof routeMiddlewareSchema>['captcha'];
}) => {
  const onSubmit: AutoFormOnSubmit<typeof formSchema> = async (
    values,
    form,
    { captchaToken }, 
  ) => {
    // Call your mutation API with captcha token
    await mutationApi({
      ...values,
      captchaToken, 
    });

    // Handle success or error
  };

  return (
    <AutoForm<typeof formSchema>
      captcha={captcha}
      fields={[]}
      onSubmit={onSubmit} 
      formSchema={formSchema}
    />
  );
};

Next, you need to set captchaToken in your mutation API call. This token is provided by the AutoForm component when the form is submitted.

plugins/{plugin_name}/src/components/form/sign-up/mutation-api.ts
'use server';

import type { z } from 'zod';

import { fetcher } from '@vitnode/core/lib/fetcher';

export const mutationApi = async ({
  captchaToken, 
  ...input
}: z.infer<typeof zodSignUpSchema> & { captchaToken }) => {
  const res = await fetcher(usersModule, {
    path: '/sign_up',
    method: 'post',
    module: 'users',
    captchaToken, 
    args: {
      body: input,
    },
  });

  if (res.status !== 201) {
    return { error: await res.text() };
  }

  const data = await res.json();

  return { data };
};

Custom Usage

If you want to use captcha in your custom form or somewhere else, follow these steps.

Activate captcha in route

plugins/{plugin_name}/src/routes/example.ts
import { buildRoute } from '@vitnode/core/api/lib/route';

export const exampleRoute = buildRoute({
  ...CONFIG_PLUGIN,
  route: {
    method: 'post',
    description: 'Create a new user',
    path: '/sign_up',
    withCaptcha: true, 
  },
  handler: async c => {},
});

Get config from middleware API

plugins/{plugin_name}/src/app/sing_up/page.tsx
import { getMiddlewareApi } from '@vitnode/core/lib/api/get-middleware-api'; 

export const SignUpView = async () => {
  const { captcha } = await getMiddlewareApi(); 

  return <FormSignUp captcha={captcha} />;
};

Use useCaptcha hook

Inside your client component, use the useCaptcha hook to handle captcha rendering and validation. Remember to add div with id="vitnode_captcha" where you want the captcha widget to appear.

plugins/{plugin_name}/src/components/form/sign-up/sign-up.tsx
'use client';

import { AutoForm } from '@vitnode/core/components/form/auto-form';

export const FormSignUp = ({
  captcha, 
}: {
  captcha: z.infer<typeof routeMiddlewareSchema>['captcha']; 
}) => {
  const { isReady, getToken, onReset } = useCaptcha(captcha);

  const onSubmit = async () => {
    await mutationApi({
      // ...other values,
      captchaToken: await getToken(), 
    });

    // Handle success or error
    onReset(); // Reset captcha after submission
  };

  return (
    <form onSubmit={onSubmit}>
      {/* Render captcha widget */}
      <div id="vitnode_captcha" />

      <Button disabled={!isReady}>Submit</Button>
    </form>
  );
};

Submit form with captcha

plugins/{plugin_name}/src/components/form/sign-up/mutation-api.ts
'use server';

import type { z } from 'zod';

import { fetcher } from '@vitnode/core/lib/fetcher';

export const mutationApi = async ({
  captchaToken, 
}: {
  captchaToken;
}) => {
  await fetcher(usersModule, {
    path: '/test',
    method: 'post',
    module: 'blog',
    captchaToken, 
  });
};