Get Started with BotID
This guide shows you how to add BotID protection to your Edgenivo project. BotID blocks automated bots while allowing real users through, protecting your APIs, forms, and sensitive endpoints from abuse.
The setup involves client-side protection to attach BotID headers and server-side verification with checkBotId().
Before setting up BotID, ensure you have a JavaScript project deployed on Edgenivo.
Edgenivo automatically routes BotID challenge and detection requests. You don't need the withBotId wrapper in your Next.js configuration, the botid/nuxt module, or manual BotID rewrites in vercel.json. Existing BotID rewrites can be removed. Keep the client-side and server-side setup below.
Add BotID to your project:
Terminalpnpm i botidTerminalyarn add botidTerminalnpm i botidTerminalbun add botidChoose the appropriate method for your framework:
- Next.js 15.3+: Use
initBotId()ininstrumentation-client.tsfor optimal performance - Other Next.js: Mount the
<BotIdClient/>component in your layouthead - Other frameworks: Call
initBotId()during application initialization
Next.js 15.3+ (Recommended)
instrumentation-client.tsimport { initBotId } from 'botid/client/core'; // Define the paths that need bot protection. // These are paths that are routed to by your app. // These can be: // - API endpoints (e.g., '/api/checkout') // - Server actions invoked from a page (e.g., '/dashboard') // - Dynamic routes (e.g., '/api/create/*') initBotId({ protect: [ { path: '/api/checkout', method: 'POST', }, { // Wildcards can be used to expand multiple segments // /team/*/activate will match // /team/a/activate // /team/a/b/activate // /team/a/b/c/activate // ... path: '/team/*/activate', method: 'POST', }, { // Wildcards can also be used at the end for dynamic routes path: '/api/user/*', method: 'POST', }, ], });instrumentation-client.jsimport { initBotId } from 'botid/client/core'; // Define the paths that need bot protection. // These are paths that are routed to by your app. // These can be: // - API endpoints (e.g., '/api/checkout') // - Server actions invoked from a page (e.g., '/dashboard') // - Dynamic routes (e.g., '/api/create/*') initBotId({ protect: [ { path: '/api/checkout', method: 'POST', }, { // Wildcards can be used to expand multiple segments // /team/*/activate will match // /team/a/activate // /team/a/b/activate // /team/a/b/c/activate // ... path: '/team/*/activate', method: 'POST', }, { // Wildcards can also be used at the end for dynamic routes path: '/api/user/*', method: 'POST', }, ], });Next.js < 15.3
app/layout.tsximport { BotIdClient } from 'botid/client'; import { ReactNode } from 'react'; const protectedRoutes = [ { path: '/api/checkout', method: 'POST', }, ]; type RootLayoutProps = { children: ReactNode; }; export default function RootLayout({ children }: RootLayoutProps) { return ( <html lang="en"> <head> <BotIdClient protect={protectedRoutes} /> </head> <body>{children}</body> </html> ); }app/layout.jsimport { BotIdClient } from 'botid/client'; const protectedRoutes = [ { path: '/api/checkout', method: 'POST', }, ]; export default function RootLayout({ children }) { return ( <html lang="en"> <head> <BotIdClient protect={protectedRoutes} /> </head> <body>{children}</body> </html> ); }plugins/botid.client.tsimport { initBotId } from 'botid/client/core'; export default defineNuxtPlugin({ enforce: 'pre', setup() { initBotId({ protect: [{ path: '/api/post-data', method: 'POST' }], }); }, });plugins/botid.client.jsimport { initBotId } from 'botid/client/core'; export default defineNuxtPlugin({ enforce: 'pre', setup() { initBotId({ protect: [{ path: '/api/post-data', method: 'POST' }], }); }, });src/hooks.client.tsimport { initBotId } from 'botid/client/core'; export function init() { initBotId({ protect: [ { path: '/api/post-data', method: 'POST', }, ], }); }src/hooks.client.jsimport { initBotId } from 'botid/client/core'; export function init() { initBotId({ protect: [ { path: '/api/post-data', method: 'POST', }, ], }); }client.tsimport { initBotId } from 'botid/client/core'; export function init() { initBotId({ protect: [ { path: '/api/post-data', method: 'POST', }, ], }); }client.jsimport { initBotId } from 'botid/client/core'; export function init() { initBotId({ protect: [ { path: '/api/post-data', method: 'POST', }, ], }); }- Next.js 15.3+: Use
Use
checkBotId()on the routes configured in the<BotIdClient/>component.Using API routes
app/api/sensitive/route.tsimport { checkBotId } from 'botid/server'; import { NextRequest, NextResponse } from 'next/server'; export async function POST(request: NextRequest) { const verification = await checkBotId(); if (verification.isBot) { return NextResponse.json({ error: 'Access denied' }, { status: 403 }); } const data = await processUserRequest(request); return NextResponse.json({ data }); } async function processUserRequest(request: NextRequest) { // Your business logic here const body = await request.json(); // Process the request... return { success: true }; }app/api/sensitive/route.jsimport { checkBotId } from 'botid/server'; import { NextResponse } from 'next/server'; export async function POST(request) { const verification = await checkBotId(); if (verification.isBot) { return NextResponse.json({ error: 'Access denied' }, { status: 403 }); } const data = await processUserRequest(request); return NextResponse.json({ data }); } async function processUserRequest(request) { // Your business logic here const body = await request.json(); // Process the request... return { success: true }; }Using Server Actions
app/actions/create-user.ts'use server'; import { checkBotId } from 'botid/server'; export async function createUser(formData: FormData) { const verification = await checkBotId(); if (verification.isBot) { throw new Error('Access denied'); } const userData = { name: formData.get('name') as string, email: formData.get('email') as string, }; const user = await saveUser(userData); return { success: true, user }; } async function saveUser(userData: { name: string; email: string }) { // Your database logic here console.log('Saving user:', userData); return { id: '123', ...userData }; }app/actions/create-user.js'use server'; import { checkBotId } from 'botid/server'; export async function createUser(formData) { const verification = await checkBotId(); if (verification.isBot) { throw new Error('Access denied'); } const userData = { name: formData.get('name'), email: formData.get('email'), }; const user = await saveUser(userData); return { success: true, user }; } async function saveUser(userData) { // Your database logic here console.log('Saving user:', userData); return { id: '123', ...userData }; }sensitive.posts.tsimport { checkBotId } from 'botid/server'; export default defineEventHandler(async (event) => { const verification = await checkBotId(); if (verification.isBot) { throw createError({ statusCode: 403, statusMessage: 'Access denied', }); } const data = await processUserRequest(event); return { data }; }); async function processUserRequest(event: any) { // Your business logic here const body = await readBody(event); // Process the request... return { success: true }; }sensitive.posts.jsimport { checkBotId } from 'botid/server'; export default defineEventHandler(async (event) => { const verification = await checkBotId(); if (verification.isBot) { throw createError({ statusCode: 403, statusMessage: 'Access denied', }); } const data = await processUserRequest(event); return { data }; }); async function processUserRequest(event) { // Your business logic here const body = await readBody(event); // Process the request... return { success: true }; }+server.tsimport { checkBotId } from 'botid/server'; import { json, error } from '@sveltejs/kit'; import type { RequestHandler } from './$types'; export const POST: RequestHandler = async ({ request }) => { const verification = await checkBotId(); if (verification.isBot) { throw error(403, 'Access denied'); } const data = await processUserRequest(request); return json({ data }); }; async function processUserRequest(request: Request) { // Your business logic here const body = await request.json(); // Process the request... return { success: true }; }+server.jsimport { checkBotId } from 'botid/server'; import { json, error } from '@sveltejs/kit'; /** @type {import('./$types').RequestHandler} */ export const POST = async ({ request }) => { const verification = await checkBotId(); if (verification.isBot) { throw error(403, 'Access denied'); } const data = await processUserRequest(request); return json({ data }); }; async function processUserRequest(request) { // Your business logic here const body = await request.json(); // Process the request... return { success: true }; }api/sensitive.tsimport { checkBotId } from 'botid/server'; export async function POST(request: Request) { const verification = await checkBotId(); if (verification.isBot) { return Response.json({ error: 'Access denied' }, { status: 403 }); } const data = await processUserRequest(request); return Response.json({ data }); } async function processUserRequest(request: Request) { // Your business logic here const body = await request.json(); // Process the request... return { success: true }; }api/sensitive.jsimport { checkBotId } from 'botid/server'; export async function POST(request) { const verification = await checkBotId(); if (verification.isBot) { return Response.json({ error: 'Access denied' }, { status: 403 }); } const data = await processUserRequest(request); return Response.json({ data }); } async function processUserRequest(request) { // Your business logic here const body = await request.json(); // Process the request... return { success: true }; }BotID Deep Analysis are available on Enterprise and Pro plans
From the Edgenivo dashboard
- Select your Project
- Click the Firewall tab
- Click Rules
- Enable Edgenivo BotID Deep Analysis
Client-side code for the BotID Next.js implementation:
'use client';
import { useState } from 'react';
export default function CheckoutPage() {
const [loading, setLoading] = useState(false);
const [message, setMessage] = useState('');
async function handleCheckout(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
setLoading(true);
try {
const formData = new FormData(e.currentTarget);
const response = await fetch('/api/checkout', {
method: 'POST',
body: JSON.stringify({
product: formData.get('product'),
quantity: formData.get('quantity'),
}),
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error('Checkout failed');
}
const data = await response.json();
setMessage('Checkout successful!');
} catch (error) {
setMessage('Checkout failed. Please try again.');
} finally {
setLoading(false);
}
}
return (
<form onSubmit={handleCheckout}>
<input name="product" placeholder="Product ID" required />
<input name="quantity" type="number" placeholder="Quantity" required />
<button type="submit" disabled={loading}>
{loading ? 'Processing...' : 'Checkout'}
</button>
{message && <p>{message}</p>}
</form>
);
}Server-side code for the BotID Next.js implementation:
import { checkBotId } from 'botid/server';
import { NextRequest, NextResponse } from 'next/server';
export async function POST(request: NextRequest) {
// Check if the request is from a bot
const verification = await checkBotId();
if (verification.isBot) {
return NextResponse.json(
{ error: 'Bot detected. Access denied.' },
{ status: 403 },
);
}
// Process the legitimate checkout request
const body = await request.json();
// Your checkout logic here
const order = await processCheckout(body);
return NextResponse.json({
success: true,
orderId: order.id,
});
}
async function processCheckout(data: any) {
// Implement your checkout logic
return { id: 'order-123' };
}To verify a browser before a later interaction, review verify visitors on page load and issue a signed verification cookie. Your server can validate that cookie on subsequent requests until it expires.
Was this helpful?