Vue 3 and Nuxt · Capstone
Capstone: A Recipe Platform with Nuxt
Design a Nuxt application for a recipe community: rendering per route, data fetching, state, API design, authentication and quality.
Scenario
A recipe community wants a new site. Visitors search and read recipes (most traffic comes from search engines); signed-in members save favourites, plan weekly menus with drag and drop, and post reviews. Editors publish featured collections daily. The team has three Vue developers and wants one codebase for pages and API.
Requirements
- Recipe pages load fast and rank in search; reviews appear within minutes.
- The menu planner is highly interactive and private.
- Members only see and edit their own favourites and menus.
- The site stays usable when the review service is slow.
Constraints
- Nuxt 4, PostgreSQL, Redis, deployed as 3 Node instances behind a load balancer.
Deliverables
- Rendering plan per route.
- Data fetching and state.
- API design.
- Authentication and authorization.
- Quality: testing and monitoring.
Hints
- Different routes deserve different rendering modes.
- Think about what's in the page payload.
Draft each deliverable below before revealing the model answer and rubric.
Further reading: Rendering modes · Data fetching
Deliverables
Describe the rendering plan.
Model answer
- Home and collection pages:
swr(minutes) or prerender with redeploy/webhook on publish. - Recipe pages:
swr: 600with Nitro cache storage in Redis shared by the 3 instances;useSeoMetaand JSON-LD for search; reviews loaded in a separate request so a slow review service doesn't block the page. - Search results: SSR without caching (or short SWR per query) and
noindexfor filtered pages. /planner/**and/account/**:ssr: false(private, interactive).
Rubric
Look for per-route choices: cached SSR for public content with shared storage, client-only rendering for the private planner.
Describe data fetching and state.
Model answer
useFetch/useAsyncDatawith explicit keys,Promise.allfor independent data,transform/pickto keep payloads small; reviews withlazy: trueand a fallback on error.- Pinia (via
@pinia/nuxt, per request) for the planner's state; server data stays in data-fetching composables, not duplicated in stores. - Composables (
useFavourites,useRecipeSearchwith debouncing) encapsulate logic and clean up after themselves.
Rubric
Strong answers avoid double fetching, keep payloads lean, handle slow dependencies, and use per-request stores only where shared client state is needed.
Describe the API design.
Model answer
server/api/recipes,/favourites,/menus,/reviewswith validated bodies and params (zod),createErrorwith proper statuses, and DTOs.- Shared logic in
server/utils(database access, authorization helpers); no HTTP calls from server to itself. - Rate limits on review posting; editor webhooks invalidate cached pages (clear Nitro cache keys).
Rubric
Look for validated endpoints, consistent errors, shared server utilities and cache invalidation on publish.
Describe authentication and authorization.
Model answer
- Sealed session cookies (nuxt-auth-utils or h3
useSession) with OAuth login; session password fromNUXT_SESSION_PASSWORD. - Every member API route calls
requireUserand filters by owner (favourites, menus, reviews); editors have a role checked on the server. - Route middleware redirects anonymous users from the planner, using
useRequestFetchduring SSR.
Rubric
Strong answers enforce ownership in every API route and treat middleware as UX, not security.
Describe testing and monitoring.
Model answer
- Vitest + Vue Test Utils for components (planner interactions, forms) and composables; API route tests against a test database.
- End-to-end tests (Playwright) for search, login, saving a favourite and planning a menu.
- Monitoring: Nitro error hooks or an error tracker, request logs with IDs, Core Web Vitals for recipe pages, and alerts on review-service latency.
Rubric
Look for component, API and end-to-end tests plus error tracking and performance monitoring.
Open in the app to mark lessons complete, take notes and track your progress.