Cloudflare Workers & Pages Functions — Birdor Cloudflare Tutorial Series (Part 5)
Leeting Yan
Cloudflare Workers allow developers to run JavaScript and TypeScript directly at the edge — close to users and without maintaining servers. Pages Functions provide the same compute model inside Cloudflare Pages projects, giving static sites like Hugo access to dynamic capabilities such as authentication, APIs, routing logic, and integrations.
This tutorial offers a calm, practical introduction to Workers and Functions with examples that fit naturally into Hugo and JAMstack projects. No prior serverless experience is required.
1. Understanding Cloudflare’s Edge Compute Model
Traditional serverless platforms execute code in centralized regions. Cloudflare Workers run code in hundreds of edge data centers, meaning:
- lower latency
- no cold starts
- consistent performance worldwide
- built-in isolation and security
- no need to choose regions
- instant global deployment
Workers use the V8 engine directly, not Node.js, which keeps them lightweight, fast, and predictable.
2. Workers vs Pages Functions (A Clear Distinction)
Cloudflare offers two ways to run serverless logic.
2.1 Cloudflare Workers
Standalone serverless applications.
Use cases:
- APIs
- proxies
- routing logic
- SSR frameworks
- authentication flows
- cron jobs
Deployment:
- via Wrangler CLI
- flexible routing
- custom domains
2.2 Pages Functions
Serverless functions specifically for Cloudflare Pages projects.
Use cases:
- small APIs for your Hugo site
- form submissions
- dynamic content
- proxying external services
- authenticated endpoints
Structure:
/functions
/api
hello.js
contact.js
Functions automatically map to routes.
They deploy together with your Hugo build.
If you’re deploying a Hugo site on Cloudflare Pages, Pages Functions are the simplest way to add dynamic behavior.
3. Creating Your First Pages Function
Inside your Hugo project repository:
mkdir -p functions/api
Create a file:
functions/api/hello.js
export async function onRequest(context) {
return new Response("Hello from Cloudflare Pages Functions!");
}
Deploy your Pages project, then visit:
/api/hello
You now have your first serverless endpoint running on Cloudflare.
4. Understanding the Function Context
Cloudflare provides a context object with:
request– incoming Request objectenv– environment variables (bindings)params– route parametersnext()– middleware continuation
Example:
export async function onRequest(context) {
const url = new URL(context.request.url);
return new Response(`You visited: ${url.pathname}`);
}
This lightweight API is intentionally simple and browser-compatible.
5. Creating Dynamic Routes
Dynamic parameters work through folder names.
Example:
functions/user/[id].js
export async function onRequest(context) {
const userId = context.params.id;
return new Response(`User ID: ${userId}`);
}
Route:
/user/42
Dynamic routing is a strength of Pages Functions for small APIs and utilities.
6. Using fetch() to call APIs
Pages Functions support the standard fetch() API.
Example: calling GitHub’s API:
export async function onRequest() {
const res = await fetch("https://api.github.com/repos/cloudflare/workers");
const data = await res.json();
return new Response(JSON.stringify(data, null, 2), {
headers: { "Content-Type": "application/json" }
});
}
This makes Pages Functions ideal as a middle layer between your Hugo site and external APIs.
7. Using Environment Variables (Bindings)
Environment variables let you store secrets and configuration.
7.1 Add bindings in Cloudflare
Cloudflare Pages → Your Project → Settings → Environment Variables
Example:
API_TOKEN = super_secret_value
7.2 Access in Functions
export async function onRequest(context) {
const token = context.env.API_TOKEN;
return new Response(`Token length: ${token.length}`);
}
Bindings never leak to the browser unless you deliberately return them.
8. Middleware: Reusable Logic
Middleware allows shared logic, such as authentication.
Example: functions/_middleware.js
export async function onRequest(context) {
const apiKey = context.request.headers.get("x-api-key");
if (apiKey !== context.env.SECRET_KEY) {
return new Response("Unauthorized", { status: 401 });
}
return await context.next();
}
This applies to all functions automatically.
9. Worker Cron Triggers (Scheduled Jobs)
Example use cases:
- clean stale KV entries
- scheduled sync with external APIs
- periodic database updates
Define in your Worker’s config:
[triggers]
crons = ["0 */6 * * *"] # every 6 hours
A scheduled Worker function might:
export default {
async scheduled(event, env, ctx) {
// do background tasks
}
}
Cron triggers are not available in Pages Functions — only in Workers.
10. Building API Routes for Hugo
Hugo remains static, but Pages Functions extend it:
Examples:
Contact form endpoint:
functions/api/contact.js
export async function onRequest({ request }) {
const data = await request.formData();
const email = data.get("email");
const message = data.get("message");
// Send to an external service or email provider
return new Response("OK");
}
Search endpoint using Meilisearch:
functions/api/search.js
export async function onRequest(context) {
const query = new URL(context.request.url).searchParams.get("q");
const res = await fetch(`${context.env.SEARCH_URL}?q=${query}`);
return new Response(await res.text(), {
headers: { "Content-Type": "application/json" }
});
}
Proxy external API:
export async function onRequest() {
return fetch("https://api.example.com/data");
}
Pages Functions turn your static Hugo site into a full-featured JAMstack app.
11. Workers: When You Need More Control
Use Workers instead of Pages Functions when you need:
- custom routing
- scheduled cron jobs
- Durable Objects (stateful logic)
- integration with R2, D1, or KV
- SSR frameworks (Astro, Qwik, SvelteKit adapters exist)
- a standalone API service
- no Pages deployment
Workers are deployed with wrangler:
npm install -g wrangler
wrangler login
wrangler init my-worker
wrangler publish
Workers offer the full power of Cloudflare’s serverless platform.
12. Integrating Workers With Pages
Cloudflare lets you apply Workers to Pages using:
- Worker overrides
- custom routing patterns
- Workers for API endpoints while Pages handles static files
This hybrid model is flexible and efficient.
13. Best Practices for Hugo + Pages Functions
- Keep Functions small and stateless
- Use bindings for secrets
- Cache API responses with
Cache APIwhere appropriate - Avoid heavy CPU work — edge compute prefers lightweight logic
- Wrap shared logic in middleware
- Serve errors responsibly (don’t expose internals)
- Use structured JSON responses
- Add logs in development (
wrangler pages dev)
For most developer tools, dashboards, and blogs, Pages Functions provide all the dynamic features needed.
14. Troubleshooting
14.1 Function not running?
Ensure:
- file is inside the
/functionsdirectory - file extension is
.jsor.ts - export name is correct (
onRequest)
14.2 Environment variable undefined?
Check:
- bindings exist in both Preview and Production environments
- variable name matches exactly
14.3 Route not matching?
Verify:
- folder structure matches expected URL path
- dynamic segments use
[param]syntax
15. Conclusion and What’s Next
In this chapter, we explored:
- Cloudflare’s edge compute model
- differences between Workers and Pages Functions
- creating serverless endpoints for Hugo
- dynamic routes, environment variables, and middleware
- API integrations and scheduled jobs
- when to use Workers instead of Functions
Pages Functions turn a static Hugo site into a dynamic, globally distributed application — without traditional backends or servers.
Next chapter:
Cloudflare Tutorial Series — Part 6: KV, R2, and D1 Storage (A Practical Guide to Edge Storage)