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
- How to plan the folders of a real app.
- How to keep all API calls in one service file.
- How to share a cart with a Pinia store.
- How to show loading and error states.
- How to validate a form and post an order.
- How to test the store and a component.
- What to check before you ship.
Step 1: Plan the Folders
File in src/ | Its one job |
|---|---|
main.ts | Creates the app, adds Pinia and the router |
router.ts | The list of pages |
api/kitchen.ts | Every HTTP call, with types |
stores/cart.ts | The cart, shared by all pages |
components/DishCard.vue | Small, reusable pieces |
views/MenuView.vue, views/CartView.vue | One file per page |
tests/cart.spec.ts | Tests, 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.tsinstead of arouter/folder.
Step 2: The Service and Loading States
api/kitchen.tswraps 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
qtyinstead of adding a second line. countandtotalare 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
computedcalledproblemexplains 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 --noEmitandnpx vitest run. Both must pass. - Run
npm run build, thennpm run previewand click through every page. - Check the browser Console for warnings, and Lighthouse for speed and accessibility, as in Performance.
- Set
VITE_API_URLfor 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.
problemcomputed = 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
| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
| URLs scattered in every page | It is quicker | One service file | Changing the server means editing everything |
| Only a data state | The API is always fast | Show loading and error too | Blank or frozen pages |
| Cart kept in one page's refs | Pages can share refs | Use a Pinia store | The cart empties on navigation |
| A duplicate line for each click | Push is enough | Raise qty for the same dish | A messy cart and wrong counts |
| Enabled button while sending | Users click once | Disable while sending | Double orders |
| Checking rules only in the browser | The form is the only way in | The server checks too | Bad data from other clients |
| Skipping tests before shipping | It worked on my machine | Run vue-tsc and vitest | Bugs 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