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/deno@1.0.0-alpha.34"; // 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/deno@1.0.0-alpha.34"; 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/deno@1.0.0-alpha.34"; // 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/deno@1.0.0-alpha.34"; 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/deno@1.0.0-alpha.34"; 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/deno@1.0.0-alpha.34"; // 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)