How Impeccable's Cloudflare Pages Deployment Works with Static API Data
Impeccable deploys to Cloudflare Pages by pre-generating static JSON files during the build process and using edge rewrite rules to serve them as dynamic API endpoints, eliminating serverless function cold starts.
The pbakaus/impeccable repository implements a static-site architecture that mimics a live API using pre-built JSON assets. By generating all API responses at build time and leveraging Cloudflare Pages' _redirects configuration, the application serves cached edge responses for typical API routes while reserving Pages Functions only for binary downloads.
Build Pipeline Architecture
The deployment strategy centers on scripts/build.js, which orchestrates the creation of static assets and Cloudflare-specific configuration files. This script transforms markdown skill definitions into a queryable JSON API during the build phase rather than at runtime.
Generating the Static API
The generateApiData() function (lines 88-119 in scripts/build.js) serializes skill metadata into four distinct JSON structures:
// scripts/build.js
function generateApiData(buildDir, skills, patterns) {
const apiDir = path.join(buildDir, '_data', 'api');
fs.mkdirSync(apiDir, { recursive: true });
// skills.json – catalogue of all skills
const skillsData = skills.map(s => ({
id: path.basename(path.dirname(s.filePath)),
name: s.name,
description: s.description,
userInvokable: s.userInvokable,
}));
fs.writeFileSync(path.join(apiDir, 'skills.json'), JSON.stringify(skillsData));
// commands.json – only user-invokable skills
const commandsData = skillsData.filter(s => s.userInvokable);
fs.writeFileSync(path.join(apiDir, 'commands.json'), JSON.stringify(commandsData));
// patterns.json – the pattern taxonomy
fs.writeFileSync(path.join(apiDir, 'patterns.json'), JSON.stringify(patterns));
// command-source/{id}.json – raw markdown source per skill
const cmdSourceDir = path.join(apiDir, 'command-source');
fs.mkdirSync(cmdSourceDir, { recursive: true });
for (const skill of skills) {
const id = path.basename(path.dirname(skill.filePath));
const content = fs.readFileSync(skill.filePath, 'utf-8');
fs.writeFileSync(path.join(cmdSourceDir, `${id}.json`), JSON.stringify({ content }));
}
console.log(`✓ Generated static API data (${skillsData.length} skills, ${commandsData.length} commands)`);
}
This function reads source files from source/skills/* and writes the processed output to build/_data/api/, creating a file-based database that Cloudflare Pages can serve directly from its edge cache.
Configuring Edge Rewrites and Headers
The generateCFConfig() function (lines 33-57) generates the _headers and _redirects files that enable the static API illusion:
// scripts/build.js
function generateCFConfig(buildDir) {
const headers = `/*
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
/api/*
Cache-Control: public, s-maxage=86400, stale-while-revalidate=3600
/_data/api/*
Cache-Control: public, s-maxage=86400, stale-while-revalidate=3600
`;
fs.writeFileSync(path.join(buildDir, '_headers'), headers);
const redirects = `/api/skills /_data/api/skills.json 200
/api/commands /_data/api/commands.json 200
/api/patterns /_data/api/patterns.json 200
/api/command-source/:id /_data/api/command-source/:id.json 200
`;
fs.writeFileSync(path.join(buildDir, '_redirects'), redirects);
console.log('✓ Generated Cloudflare Pages config (_headers, _redirects)');
}
The 200 status code in the redirect rules is critical—it instructs Cloudflare Pages to rewrite the URL internally rather than issue an HTTP redirect, preserving the /api/ path in the browser while serving the static JSON file.
Runtime Behavior on Cloudflare Pages
When deployed, Cloudflare Pages handles different request types through distinct mechanisms:
- Static API requests (e.g.,
/api/skills) match the rewrite rules in_redirectsand serve the corresponding JSON frombuild/_data/api/with long-term edge caching. - Binary downloads (e.g.,
/api/download/...) route to Pages Functions located infunctions/api/download/[type]/[provider]/[id].js, which access bundled files viaenv.ASSETS.fetch().
The build script copies provider-specific bundles into build/_data/dist via copyDistToBuild(), making them available to Functions through the Cloudflare Assets binding:
// functions/api/download/[type]/[provider]/[id].js
export async function onRequest(context) {
const { ASSETS } = context.env; // Cloudflare Pages Assets
const path = `/_data/dist/${params.provider}/${params.id}`;
const asset = await ASSETS.fetch(path);
return new Response(await asset.arrayBuffer(), {
headers: { 'Content-Type': asset.headers.get('Content-Type') },
});
}
This hybrid approach ensures that read-heavy API traffic hits cached static assets, while dynamic download requests execute only when necessary.
End-to-End Deployment Workflow
The complete Impeccable Cloudflare Pages deployment follows this sequence:
- Build initiation:
bun run buildexecutesscripts/build.js. - Data processing: Skills are read from
source/skills/, transformed for each provider, and written todist/. - API generation:
generateApiDatacreates the static JSON structure underbuild/_data/api/. - Asset staging:
copyDistToBuildcopies thedist/folder intobuild/_data/dist. - Configuration:
generateCFConfigwrites_headersand_redirectsto the build root. - Deployment: Cloudflare Pages deploys the
build/folder as specified inwrangler.toml:
# wrangler.toml
name = "impeccable-style"
compatibility_date = "2024-12-01"
pages_build_output_dir = "./build"
- Runtime serving: Requests to
https://<site>.pages.dev/api/skillsare rewritten to_data/api/skills.jsonand served from Cloudflare's edge cache with a 24-hour TTL.
Summary
- Static generation occurs during the build phase via
generateApiData()inscripts/build.js, creating JSON files for skills, commands, and patterns. - Edge rewrites configured in
_redirectsmap/api/*routes to static files using 200-status internal rewrites. - Aggressive caching is applied via
_headers, settings-maxage=86400andstale-while-revalidatefor API endpoints. - Hybrid architecture serves metadata from static assets and binary downloads via Pages Functions using
env.ASSETS.fetch(). - Zero cold starts for API reads because static assets are served directly from Cloudflare's cache without invoking serverless functions.
Frequently Asked Questions
How does Impeccable handle dynamic API requests on a static platform?
Impeccable eliminates truly dynamic API requests by pre-generating all possible JSON responses during the build process. The _redirects file uses 200-status rewrites to mask the static file paths, so clients interact with /api/skills while Cloudflare serves the pre-built _data/api/skills.json from its edge cache.
What is the purpose of the _headers file in the build output?
The _headers file configures HTTP response headers for the static assets. It applies security headers globally and sets Cache-Control: public, s-maxage=86400, stale-while-revalidate=3600 specifically for /api/* routes, ensuring responses remain cached at Cloudflare's edge for 24 hours with a one-hour stale-while-revalidate window.
Why does the build script copy files to both dist/ and build/_data/dist/?
The dist/ directory contains provider-specific skill bundles (e.g., .claude/skills/...) used for local development, while build/_data/dist/ is the copy consumed by Cloudflare Pages Functions at runtime. The copyDistToBuild() function ensures these files are available within the Pages build output directory so that env.ASSETS.fetch() can retrieve them without external network requests.
Can the static API data be updated without a full rebuild?
No. Because the API responses are static JSON files generated during the build process, any change to skill metadata in source/skills/* requires running bun run build again and redeploying to Cloudflare Pages. This trade-off prioritizes read performance and cacheability over real-time data updates.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →