Capstone: Build Meera's Kitchen

By Syncfusion®, Inc. · Last updated

Putting the whole course together in one app: a folder plan, a typed API service, a Pinia cart store, routed pages with loading and error states, a validated checkout form that posts an order, and tests for the store and a component.

The Project

  • You have learned each part of Vue one lesson at a time. Now you build one app with all of them.
  • Meera's Kitchen lets a customer see the menu, add dishes to a cart, pick a table and place an order.
  • It uses components, props and events, Vue Router, Pinia, HTTP requests, form validation and tests.
  • Each step below adds one part. The first lab is the finished app, so you can see where you are going.

Simple lesson: small, clear parts, each with one job, make a big app easy to change.

A Real-World Example

  • Click Add on a few dishes, open Cart, pick a table and place the order.
  • Watch the Console: every request to the pretend API is listed.

In a real project, start with npm create vue@latest, choose TypeScript, Router, Pinia and Vitest, then add axios. The files below go in src/. This lab runs the same files in the preview, with the pretend API at api.meeraskitchen.dev.

What You Will Learn

  1. How to plan the folders of a real app.
  2. How to keep all API calls in one service file.
  3. How to share a cart with a Pinia store.
  4. How to show loading and error states.
  5. How to validate a form and post an order.
  6. How to test the store and a component.
  7. What to check before you ship.

Step 1: Plan the Folders

File in src/Its one job
main.tsCreates the app, adds Pinia and the router
router.tsThe list of pages
api/kitchen.tsEvery HTTP call, with types
stores/cart.tsThe cart, shared by all pages
components/DishCard.vueSmall, reusable pieces
views/MenuView.vue, views/CartView.vueOne file per page
tests/cart.spec.tsTests, run with npx vitest
  • views/ hold pages. components/ hold pieces used inside pages.
  • api/ is the only place that knows the server's address. Pages call functions, not URLs.
  • stores/ hold data that more than one page needs.
  • Each file has one job. When something breaks, you know where to look.
  • This follows the layout from Errors, Security and Deployment, kept flat for a small app: one router.ts instead of a router/ folder.

Step 2: The Service and Loading States

  • api/kitchen.ts wraps Axios. It returns plain typed data, not responses.
  • A page tracks three states: loading, error and data. Show exactly one.
  • Try it below: the menu loads slowly, then the second button asks a broken address.

Step 3: The Cart Store

  • The cart lives in Pinia, so the menu page and the cart page see the same data.
  • Adding a dish twice raises its qty instead of adding a second line.
  • count and total are computed, so they are always right.

Keep the store's rules (like "same dish raises qty") inside the store, not in the pages. Then every page gets them for free, and one test checks them.

Step 4: Checkout With Validation

  • A computed called problem explains what is wrong, or is empty when all is well.
  • The button is disabled while there is a problem or while sending, so the order cannot be sent twice.
  • After a good reply, show the order number and clear the cart.

The browser check is for the user's comfort. The server must check the same rules again, because anyone can send a request without using your form.

Step 5: Test It

  • Test the store's rules and the components' behaviour, as in Testing Vue Apps.
  • setActivePinia(createPinia()) gives each test a fresh store.

Step 6: Before You Ship

  • Run npx vue-tsc --noEmit and npx vitest run. Both must pass.
  • Run npm run build, then npm run preview and click through every page.
  • Check the browser Console for warnings, and Lighthouse for speed and accessibility, as in Performance.
  • Set VITE_API_URL for production, and add the SPA fallback on your host, as in Errors, Security and Deployment.
  • Add an error handler (app.config.errorHandler) so crashes are reported.

Simple lesson: plan, build in small steps, test, then ship with checks.

TL;DR

  • views/ = one file per page.
  • components/ = small pieces used inside pages.
  • api/ = the only place with URLs. Returns typed data.
  • stores/ = data shared by several pages, with Pinia.
  • Three states = loading, error, data. Show exactly one.
  • problem computed = explains what is wrong with the form, or is empty.
  • Disable while sending = stops double orders.
  • Tests = store rules and component behaviour, with a fresh Pinia per test.
  • Before shipping = vue-tsc, vitest, build, preview, Console check.

Common mistakes

MistakeWrong ideaCorrect ideaWhat goes wrong
URLs scattered in every pageIt is quickerOne service fileChanging the server means editing everything
Only a data stateThe API is always fastShow loading and error tooBlank or frozen pages
Cart kept in one page's refsPages can share refsUse a Pinia storeThe cart empties on navigation
A duplicate line for each clickPush is enoughRaise qty for the same dishA messy cart and wrong counts
Enabled button while sendingUsers click onceDisable while sendingDouble orders
Checking rules only in the browserThe form is the only way inThe server checks tooBad data from other clients
Skipping tests before shippingIt worked on my machineRun vue-tsc and vitestBugs reach customers

Check Yourself

Exercises

What to Remember

  • Give each folder and file one job: views, components, api, stores, tests.
  • Keep every URL in one service file that returns typed data.
  • Handle loading, error and data on every page that fetches.
  • Share the cart with Pinia, validate with a computed, and disable while sending.
  • Run vue-tsc, vitest and a preview build before you ship.

Next: Vue Cheat Sheet