How I Built an AI Prompt Generator with Next.js 15, Cloudflare Pages, and DeepSeek
Building AI tools in 2026 is easier than ever — but getting the architecture right takes some thought. Here's how I built a free AI Prompt Generator that supports 4 models, 6 languages, and handles rate limiting at scale.
The Problem
I wanted a prompt builder that:
- Works offline-first for basic prompt construction
- Uses AI only when explicitly requested (cost control)
- Supports multiple models with different field sets
- Handles rate limiting per IP without a database
- Deploys to a global CDN for free
Architecture Overview
Frontend: Next.js 15 Static Export
I used Next.js 15 with output: 'export' mode — this generates static HTML/CSS/JS that can be served from any CDN. The core prompt building is pure frontend:
interface PromptField {
key: string;
options: string[];
}
interface PromptModel {
id: string;
kind: 'image' | 'video';
fields: PromptField[];
join: (values: Record<string, string>, lang: 'en' | 'zh') => string;
}
Each model has its own field configuration and join function that assembles the prompt. The join function takes the user's selected options and outputs either English or Chinese prompts based on a toggle.
Backend: Cloudflare Pages Functions
For the AI refinement, I used Cloudflare Pages Functions as my serverless backend. The key insight here is that Cloudflare Functions run at edge locations worldwide, so latency is minimal.
// functions/api/[[path]].ts
case '/api/prompt-refine': {
const prompt = payload.prompt as string;
const kind = (payload.kind as string) || 'image';
const lang = (payload.lang as string) || 'en';
const rateLimitResult = await checkRateLimit(env, request, 10);
if (rateLimitResult.blocked) {
return json({ error: 'RATE_LIMIT', message: rateLimitResult.message }, 429);
}
const content = await callDeepseek(env, systemPrompt, userPrompt, true);
// ...
}
Rate Limiting: D1 Database
For rate limiting, I used Cloudflare D1 (a SQLite-compatible serverless database). Each IP gets a counter keyed by ${ip}:${day}. No Redis needed — D1 is fast enough for this use case.
async function checkRateLimit(env, request, limit) {
const ip = request.headers.get('CF-Connecting-IP');
const day = new Date().toISOString().split('T')[0];
const key = `${ip}:${day}`;
const { results } = await env.DB.prepare(
'SELECT count FROM ai_usage WHERE key = ?'
).bind(key).run();
// ... increment and check
}
AI Integration: DeepSeek API
I chose DeepSeek for its JSON response mode — no parsing markdown or extracting text from code blocks. The system prompt instructs the AI to return valid JSON only:
Top comments (0)