Designing a Recipe API and UI That Works Together
How RecipeScrape's public JSON API and warm editorial UI were designed as two sides of the same coin — one machine-friendly, one human-friendly.
RecipeScrape has two faces: a public JSON API for developers building cooking apps, and a browsing UI for anyone who wants to explore the recipe collection. Both are served from the same Next.js app, backed by the same database queries.
The API: RESTful and Discoverable
The API lives under /api/v1/ as Next.js Route Handlers — no separate server, no Express, no FastAPI. Every endpoint returns JSON with consistent shapes:
| Endpoint | What it gives you |
|---|---|
GET /api/v1/recipes | Paginated list with search, filters, full-text query |
GET /api/v1/recipes/random | One random recipe (with optional ?source= filter) |
GET /api/v1/recipes/:slug | Single recipe detail |
GET /api/v1/sources | All scraped sites with counts and last-scraped timestamps |
GET /api/v1/categories | Distinct category values |
GET /api/v1/cuisines | Distinct cuisine values |
GET /api/v1/health | Status, total count, last scrape time |
The list endpoint supports query parameters for q (full-text search), source, cuisine, category, max_time, limit, and offset. Every response includes total, limit, and offset so clients can build pagination without guessing.
Recipes are returned as structured JSONB — ingredients are arrays of {name, quantity, unit, notes}, instructions are arrays of {step, text}, and nutrition is a flat object with calories, protein_g, fat_g, carbs_g, fiber_g. This structure means consuming apps can scale servings client-side, render ingredient lists, and display step-by-step instructions without parsing text.
The UI: Warm, Editorial, and Functional
The design system takes cues from food magazines rather than traditional developer docs:
- Palette: Near-black
#0f0f0f, warm off-white#f5f0e8, saffron-amber accent#E8A838 - Display type: Fraunces (variable serif for an editorial food feel)
- Body type: DM Sans (clean and readable)
- Code: JetBrains Mono (for API examples)
Recipe cards feature a signature style — the category label is pinned to the top-left corner, rotated slightly, set in amber-background all-caps Fraunces italic. It gives the grid a hand-stamped, artisanal feel that matches the food content.
Pages
- Home — A hero with the tagline "Every recipe. One API." plus a live count of recipes in the database and a
curlexample highlighted with Shiki. - Browse — Full-width search bar with debounced client-side fetching, sidebar filters, infinite scroll via IntersectionObserver.
- Detail — Full-width hero image, two-column ingredients/instructions layout, nutrition card, source attribution badge, and a "Copy API endpoint" button.
- Sources — Cards per scraped site with recipe count and last-scraped timestamp.
- API Docs — Hand-written reference with cURL, JavaScript
fetch, and Pythonhttpxexamples. No Swagger embed — just clear, real-world usage.
The API Docs Page
Every API route is documented on the /api-docs page with request/response schemas, query parameters table, and code examples in three languages. Syntax highlighting uses Shiki (the same engine powering VS Code's syntax highlighting). The docs page is a regular React component — easy to maintain and extend alongside the routes it documents.