Table of Contents
- Introduction
- Prerequisites
- Setup and Installation
- Basic Implementation
- Security Features
- Advanced Patterns
- Testing Locally
- Deployment
- Best Practices
- Troubleshooting
Introduction
What is Arcjet?
Arcjet helps developers protect their apps in just a few lines of code. Implement bot protection, rate limiting, email verification, PII detection, & defend against common attacks. It's a security-as-code SDK that integrates directly into your application, providing protection at the edge.
What are Netlify Edge Functions?
Edge Functions connect the Netlify platform and workflow with an open runtime standard at the network edge. This enables you to build fast, personalized web experiences with an ecosystem of development tools. They run on the Deno runtime and execute close to your users for optimal performance.
Why Use Arcjet with Netlify Edge Functions?
- Performance: Both run at the edge, minimizing latency
- Security: Add protection without additional infrastructure
- Developer Experience: Simple integration with just a few lines of code
- Flexibility: Configure different rules for different routes
Prerequisites
Before starting, ensure you have:
- Node.js 18+ installed
- A Netlify account
- The Netlify CLI installed:
npm install -g netlify-cli
- An Arcjet account (free tier available at arcjet.com)
- Basic knowledge of JavaScript/TypeScript
Setup and Installation
Step 1: Create a New Netlify Site
# Create a new directory for your project
mkdir my-secure-edge-app
cd my-secure-edge-app
# Initialize a new Netlify site
netlify init
# Create the edge functions directory
mkdir -p netlify/edge-functions
Step 2: Configure Netlify
Create a netlify.toml
file in your project root:
[build]
publish = "public"
[[edge_functions]]
path = "/api/*"
function = "api-handler"
[[edge_functions]]
path = "/protected/*"
function = "protected-route"
Step 3: Set Up Arcjet
- Sign up for a free Arcjet account at app.arcjet.com
- Create a new site in the Arcjet dashboard
- Copy your API key
- Add the key to your Netlify environment variables:
# Using Netlify CLI
netlify env:set ARCJET_KEY "your-arcjet-key-here"
Basic Implementation
Creating Your First Protected Edge Function
Create netlify/edge-functions/api-handler.ts
:
import arcjet, { shield, tokenBucket } from "https://esm.sh/@arcjet/[email protected]";
// Initialize Arcjet with your site key
const aj = arcjet({
key: Deno.env.get("ARCJET_KEY")!,
characteristics: ["ip"], // Track by IP address
rules: [
// Shield protects against common attacks
shield({
mode: "LIVE", // Use "DRY_RUN" for testing
}),
// Rate limiting with token bucket
tokenBucket({
mode: "LIVE",
refillRate: 10, // 10 tokens per interval
interval: 60, // Per minute
capacity: 50, // Maximum tokens
}),
],
});
export default async (request: Request, context: Context) => {
// Protect the request
const decision = await aj.protect(request);
if (decision.isDenied()) {
return new Response(
JSON.stringify({
error: "Forbidden",
reason: decision.reason
}),
{
status: 403,
headers: { "Content-Type": "application/json" }
}
);
}
// Your API logic here
return new Response(
JSON.stringify({
message: "Hello from protected API!",
geo: context.geo // Netlify provides geolocation data
}),
{
status: 200,
headers: { "Content-Type": "application/json" }
}
);
};
export const config = { path: "/api/*" };
Security Features
1. Bot Detection
Protect against automated attacks and scrapers:
import arcjet, { detectBot } from "https://esm.sh/@arcjet/[email protected]";
const aj = arcjet({
key: Deno.env.get("ARCJET_KEY")!,
rules: [
detectBot({
mode: "LIVE",
allow: [], // Explicitly allow no bots
// Or allow specific bots:
// allow: ["GOOGLE_CRAWLER", "BING_CRAWLER"],
}),
],
});
2. Rate Limiting Patterns
Different rate limiting strategies for various use cases:
import arcjet, { fixedWindow, slidingWindow, tokenBucket } from "https://esm.sh/@arcjet/[email protected]";
// Fixed window - Simple time-based limits
const fixedWindowRule = fixedWindow({
mode: "LIVE",
window: "1h", // 1 hour window
max: 100, // 100 requests per window
});
// Sliding window - More accurate rate limiting
const slidingWindowRule = slidingWindow({
mode: "LIVE",
interval: 60, // 60 seconds
max: 10, // 10 requests per interval
});
// Token bucket - Allows bursts
const tokenBucketRule = tokenBucket({
mode: "LIVE",
refillRate: 1, // 1 token per interval
interval: 1, // Every second
capacity: 10, // Burst capacity
});
3. Email Validation
Validate email addresses at the edge:
import arcjet, { validateEmail } from "https://esm.sh/@arcjet/[email protected]";
const aj = arcjet({
key: Deno.env.get("ARCJET_KEY")!,
rules: [], // Email validation is called separately
});
export default async (request: Request) => {
const body = await request.json();
const email = body.email;
// Validate email
const emailDecision = await aj.validateEmail(email);
if (!emailDecision.isValid()) {
return new Response(
JSON.stringify({
error: "Invalid email",
details: emailDecision.details
}),
{ status: 400 }
);
}
// Continue with valid email...
};
4. Sensitive Information Detection
Detect and redact PII:
import arcjet, { sensitiveInfo } from "https://esm.sh/@arcjet/[email protected]";
const aj = arcjet({
key: Deno.env.get("ARCJET_KEY")!,
rules: [
sensitiveInfo({
mode: "LIVE",
detect: ["EMAIL", "PHONE", "CREDIT_CARD"],
redact: true,
}),
],
});
Advanced Patterns
Per-User Rate Limiting
Track rate limits by authenticated user:
export default async (request: Request, context: Context) => {
// Extract user ID from JWT or session
const userId = await getUserIdFromRequest(request);
const aj = arcjet({
key: Deno.env.get("ARCJET_KEY")!,
characteristics: ["userId"], // Track by user ID
rules: [
tokenBucket({
mode: "LIVE",
refillRate: 100,
interval: 3600, // Per hour
capacity: 100,
}),
],
});
const decision = await aj.protect(request, { userId });
if (decision.isDenied()) {
return new Response("Rate limit exceeded", { status: 429 });
}
// Process request...
};
Geolocation-Based Rules
Use Netlify's geo data with Arcjet:
export default async (request: Request, context: Context) => {
const country = context.geo?.country?.code || "UNKNOWN";
// Apply stricter limits for certain regions
const rules = country === "SUSPICIOUS_REGION"
? [tokenBucket({ mode: "LIVE", refillRate: 1, interval: 60, capacity: 5 })]
: [tokenBucket({ mode: "LIVE", refillRate: 10, interval: 60, capacity: 50 })];
const aj = arcjet({
key: Deno.env.get("ARCJET_KEY")!,
rules,
});
const decision = await aj.protect(request);
// Handle decision...
};
Conditional Protection
Apply different rules based on routes:
export default async (request: Request, context: Context) => {
const url = new URL(request.url);
const path = url.pathname;
// Stricter rules for sensitive endpoints
const rules = path.startsWith("/admin")
? [
shield({ mode: "LIVE" }),
tokenBucket({ mode: "LIVE", refillRate: 1, interval: 60, capacity: 10 }),
detectBot({ mode: "LIVE", allow: [] }),
]
: [
shield({ mode: "LIVE" }),
tokenBucket({ mode: "LIVE", refillRate: 10, interval: 60, capacity: 100 }),
];
const aj = arcjet({
key: Deno.env.get("ARCJET_KEY")!,
rules,
});
// Continue with protection...
};
Testing Locally
Using Netlify Dev
You can use Netlify CLI to test edge functions locally before deploying them to Netlify.
# Start local development server
netlify dev
# Your edge functions will be available at:
# http://localhost:8888/api/*
# http://localhost:8888/protected/*
Testing Arcjet Rules
Create a test script test-protection.js
:
// Test rate limiting
async function testRateLimit() {
const endpoint = "http://localhost:8888/api/test";
for (let i = 0; i < 15; i++) {
const response = await fetch(endpoint);
console.log(`Request ${i + 1}: ${response.status}`);
if (response.status === 403) {
const body = await response.json();
console.log("Blocked:", body.reason);
}
}
}
// Test bot detection
async function testBotDetection() {
const response = await fetch("http://localhost:8888/api/test", {
headers: {
"User-Agent": "bot/1.0"
}
});
console.log("Bot test:", response.status);
}
testRateLimit();
testBotDetection();
Deployment
Deploy to Netlify
# Deploy to production
netlify deploy --prod
# Or use Git-based deployments
git push origin main
Monitor in Arcjet Dashboard
After deployment:
- Visit your Arcjet dashboard
- View real-time analytics
- Monitor blocked requests
- Adjust rules as needed
Best Practices
1. Start with DRY_RUN Mode
When in DRY_RUN mode, each rule will return its decision, but the end conclusion will always be ALLOW. This allows you to run Arcjet in passive / demo mode to test rules before enabling them.
// Start with DRY_RUN for testing
shield({ mode: "DRY_RUN" })
// Switch to LIVE when ready
shield({ mode: "LIVE" })
2. Use Custom Characteristics
Track requests by meaningful identifiers:
const aj = arcjet({
key: Deno.env.get("ARCJET_KEY")!,
characteristics: ["userId", "apiKey", "ip"],
rules: [
tokenBucket({
mode: "LIVE",
refillRate: 100,
interval: 3600,
capacity: 1000,
}),
],
});
// Pass characteristics when protecting
const decision = await aj.protect(request, {
userId: user.id,
apiKey: apiKey,
});
3. Handle Errors Gracefully
try {
const decision = await aj.protect(request);
if (decision.isDenied()) {
// Return appropriate error response
return new Response("Forbidden", { status: 403 });
}
} catch (error) {
// Arcjet fails open by default
console.error("Arcjet error:", error);
// Continue processing the request
}
4. Optimize for Performance
- Initialize Arcjet outside request handlers
- Use appropriate caching strategies
- Minimize custom characteristic calculations
Troubleshooting
Common Issues
1. ARCJET_KEY not found
// Check if key is set
if (!Deno.env.get("ARCJET_KEY")) {
console.error("ARCJET_KEY environment variable not set");
}
2. Import errors
Make sure to use the correct import URL:
// Correct
import arcjet from "https://esm.sh/@arcjet/[email protected]";
// Incorrect (npm: prefix doesn't work reliably in all cases)
import arcjet from "npm:@arcjet/deno";
3. Local development issues
If you see this error then you are probably running an older version of Next.js. For Netlify Edge Functions, ensure you're using the latest Netlify CLI.
Debug Mode
Enable debug logging:
const aj = arcjet({
key: Deno.env.get("ARCJET_KEY")!,
rules: [...],
// Enable debug logging
log: {
level: "debug"
}
});
Getting Help
- Arcjet Documentation: docs.arcjet.com
- Netlify Edge Functions Docs: docs.netlify.com/edge-functions
- Arcjet Support: support.arcjet.com
- Netlify Support: support.netlify.com
Conclusion
You've now learned how to integrate Arcjet security features with Netlify Edge Functions. This combination provides:
- Edge-native security: Protection runs close to your users
- Flexible rules: Configure different protections for different routes
- Easy integration: Just a few lines of code
- Comprehensive protection: Shield against bots, attacks, and abuse
Start with basic protection and gradually add more sophisticated rules as your application grows. Remember to monitor your Arcjet dashboard to understand traffic patterns and adjust rules accordingly.
Top comments (0)