ViteReactSaaS

fullstack-intern-task — Vite SaaS模板

作者 Shanmugamrskfamily

Layora 是一个迷你 SaaS 模板商店全栈示例:React + Vite 前端、Express + MongoDB API,支持 JWT 登录、收藏、搜索筛选与演示结算。

看看你的网站会是什么样

浏览真实页面截图,比较内容排版与手机效果。点击下方缩略图切换,放大查看整页细节。

2 个预览视图
桌面端首页1 / 2
桌面端截图首屏预览

由服务器浏览器实际访问页面后截取。

全部页面与视图 2

截图用于了解视觉与布局,交互效果请以实际项目为准。

采集于 2026-10-10查看来源

值得关注的模板亮点

从已有项目资料中,快速了解它的特点。

01

React + Vite 前端与 Express + MongoDB 后端组成的全栈模板商店示例

02

支持搜索、分类筛选、免费/付费过滤和多种排序,筛选状态保存在 URL 中

03

JWT 认证与 bcrypt 密码哈希,接口使用 Zod 校验请求参数

04

收藏功能带乐观更新与撤销,演示结算展示原价、优惠、18% GST 和总价

05

内置 16 个模板、6 个分类的种子数据,并提供 Swagger API 文档

06

浅色/深色主题与移动端适配,演示 API 空闲休眠时前端显示唤醒提示

模板详细介绍

Layora 是一个用于演示 SaaS 模板商店流程的全栈项目。前端使用 React 与 Vite 构建,配合 Tailwind CSS 实现浅色/深色主题与响应式布局;后端使用 Express 与 MongoDB(Mongoose),提供模板、认证、收藏和订单接口。

功能上,商店包含模板目录(支持搜索、分类筛选、免费/付费过滤、按热度/日期/名称/价格排序,筛选条件写入 URL)、模板详情弹窗、收藏(含乐观更新与撤销)、演示结算(展示原价、优惠、18% GST 与总价,不涉及真实支付和卡号)、我的库(订单号、已付金额与节省金额)以及登录注册。认证使用 JWT 与 bcrypt 哈希密码,接口用 Zod 做请求校验。

项目附带 16 个模板、6 个分类的种子数据,并提供 Swagger 文档与健康检查接口。演示 API 部署在 Render 免费层,空闲时会休眠,首次请求可能等待较久,前端会显示唤醒提示并自动重试。仓库采用 MIT 许可,但演示图片、字体、图标等第三方素材需单独核对许可。

ViteReactSaaS

适合你的项目吗?

适用场景

  • 学习或演示 React 与 Express 前后端分离项目的接口对接方式
  • 参考 JWT 认证、收藏与订单等常见 SaaS 商店功能的实现结构
  • 作为全栈实习任务或课程作业的参考实现
  • 在本地运行并浏览模板目录、详情、收藏和演示结算流程

适合人群

  • 正在学习全栈开发并希望查看完整示例的开发者
  • 需要参考模板商店类项目结构的前端或后端学习者
  • 想了解 JWT 认证、Zod 校验和 MongoDB 数据建模的开发者

上手与定制指南

结合仓库当前版本的说明,按下面的资料检查内容、配置与运行要求。

  1. 1

    仓库包含 client 与 server 两个部分,需分别安装依赖并配置环境变量(如 MongoDB 连接串与 JWT 密钥)

  2. 2

    演示账号为 [email protected] / Demo@1234,该账号由所有人共享,建议自行注册新账号

  3. 3

    演示 API 部署在 Render 免费层,空闲休眠后首次请求可能等待较久,本地运行可避免此问题

  4. 4

    演示结算不涉及真实支付,不会收集卡号信息

  5. 5

    代码采用 MIT 许可,但演示图片、字体、图标等第三方素材需按各自许可单独核对

查看仓库使用说明

授权与来源资料

使用条件与限制

免费使用 · 保留许可声明

本次收录的是公开仓库中采用 MIT 许可的免费版本;免费范围为该版本代码,不代表外部服务或第三方素材免费。

使用时需要遵守

  • 允许免费使用、修改、商用和分发该版本代码。
  • 分发代码副本或实质性部分时,保留原作者版权声明和完整 MIT 许可声明。
  • 代码按现状提供;担保与责任限制以原许可证及适用法律为准。

额外费用与待核对事项

  • 演示图片、字体、图标及其他第三方素材需按各自许可核对,不能仅凭代码的 MIT 许可判断。
  • 域名、托管、数据库、支付或 AI 等外部服务可能另外收费。
  • 演示 API 部署在 Render 免费层,空闲时会休眠,首次请求可能需等待约 50 秒,前端会显示唤醒提示并自动重试。
  • 演示账号由所有人共享,初始含三个收藏和两个库内模板;如需干净数据请自行注册新账号。
  • 结算为演示流程,不涉及真实支付,也不会要求填写卡号信息。
  • 使用自有 MongoDB 时需复制环境变量示例文件并配置 MONGODB_URI 连接串。
  • 部署到 Render 时需在控制台自行设置 MONGODB_URI 与 JWT_SECRET 等密钥,仓库中不包含这些值。
  • 未配置 MONGODB_URI 时 API 会启动内存版 MongoDB 并写入种子数据,首次运行需下载 MongoDB 二进制文件。
查看原文依据

Permission is hereby granted, free of charge

许可证原文

The API runs on Render's free tier, which sleeps when idle, so the very first request can take up to ~50 seconds.

README 原文

**Demo account:** `[email protected]` / `Demo@1234`, or click **Continue with demo account** on the sign-in page. It starts with three favourites and two templates in its library, and it is shared by everyone; create your own account for a clean shelf.

README 原文

**Demo checkout.** In a paid template, press **Buy**: the order summary shows the M.R.P., the launch-offer saving, 18% GST and the total. **Confirm order** adds it to your library. No payment is taken and no card details are asked for.

README 原文

**Use your own MongoDB:** copy `server/.env.example` to `server/.env` and set `MONGODB_URI`.

README 原文

Secrets (`MONGODB_URI`, `JWT_SECRET`) are set in the Render dashboard, never committed.

README 原文

**No database setup needed.** Without `MONGODB_URI`, the API starts an in-memory MongoDB and seeds 16 templates and the demo account (the first run downloads a MongoDB binary).

README 原文
https://github.com/Shanmugamrskfamily/fullstack-intern-task/blob/main/LICENSEhttps://github.com/Shanmugamrskfamily/fullstack-intern-task/blob/main/README.md

DeepSeek 根据仓库许可证和 README 整理 ·2026-10-10。结果仅覆盖已读取资料,依赖、图片、字体及后续条款变更以各自原文为准。

项目核对记录与介绍依据

模板资料

来源平台
GitHub
资料与维护参考分
资料较完整 · 85 / 100
技术栈
Vite · React
网站类型
SaaS
这个分数如何计算

本站按许可资料(最高30分)、演示链接(12分)、预览图(10分)、Star数(15分)、记录的维护时间(15分)、描述完整度(8分)、功能记录(5分)和分类资料(5分)计算。它不是代码、安全或性能测试,也不是 Google 评分;有链接不代表已实测可用。

这个项目需要核对什么

记录的仓库更新时间
2026-10-10
截图检查日期
2026-10-10

已收录演示链接。请用你的内容与屏幕尺寸检查;有演示不代表表单、认证或支付已经可用。

目录记录的主要技术栈是 Vite。请通过依赖清单和锁文件核对实际版本与托管要求。这些说明基于收录资料,不代表代码审计或部署实测。

查看具体选型方法 →

介绍的制作与核对状态

介绍由 AI 辅助整理目录与仓库资料。仓库作者不等于这篇介绍的审核者。

审核声明: [email protected] · 2026-10-10

查看当时使用的来源资料

SHA-256: 969da3ca028fef9ada8ddef53f746fe4238d19eb6ffeb2e4f874bbb6fcb801f5

2026-10-10T09:27:32.955Z

来源文件 / 仓库
<div align="center">

<img src="client/public/icon-192.png" width="76" height="76" alt="Layora logo" />

# Layora · Mini SaaS Template Store

A full-stack template store: browse, search and filter SaaS templates priced in INR, keep a shelf of favourites, and buy or claim templates into your own library through a demo checkout.

**[Live app](https://layora-template-store.vercel.app)** · **[Live API](https://layora-api.onrender.com/api/health)** · **[API docs (Swagger)](https://layora-api.onrender.com/api/docs)**

[![CI](https://github.com/Shanmugamrskfamily/fullstack-intern-task/actions/workflows/ci.yml/badge.svg)](https://github.com/Shanmugamrskfamily/fullstack-intern-task/actions/workflows/ci.yml)
![React 19](https://img.shields.io/badge/React-19-61DAFB?logo=react&logoColor=white)
![Vite 8](https://img.shields.io/badge/Vite-8-646CFF?logo=vite&logoColor=white)
![Tailwind CSS 4](https://img.shields.io/badge/Tailwind_CSS-4-06B6D4?logo=tailwindcss&logoColor=white)
![Express 5](https://img.shields.io/badge/Express-5-000000?logo=express&logoColor=white)
![MongoDB](https://img.shields.io/badge/MongoDB-Mongoose_9-47A248?logo=mongodb&logoColor=white)
![TypeScript](https://img.shields.io/badge/TypeScript-strict-3178C6?logo=typescript&logoColor=white)

</div>

![Layora demo: launch offer, demo checkout, My Library and dark mode](docs/demo.gif)

<sub>Browse the launch offer → open a template → demo checkout (M.R.P., saving, 18% GST) → My Library → dark mode.</sub>

## Live links

|                       | URL                                                         |
| --------------------- | ----------------------------------------------------------- |
| Web app (Vercel)      | **https://layora-template-store.vercel.app**                |
| API base (Render)     | https://layora-api.onrender.com/api                         |
| API docs (Swagger UI) | https://layora-api.onrender.com/api/docs                    |
| Health check          | https://layora-api.onrender.com/api/health                  |
| Repository            | https://github.com/Shanmugamrskfamily/fullstack-intern-task |

**Demo account:** `[email protected]` / `Demo@1234`, or click **Continue with demo account** on the sign-in page. It starts with three favourites and two templates in its library, and it is shared by everyone; create your own account for a clean shelf.

> The API runs on Render's free tier, which sleeps when idle, so the very first request can take up to ~50 seconds. The app shows a **"Waking up the server…"** banner with automatic retries instead of a broken page, and a scheduled GitHub Action pings the API every 10 minutes to keep it warm.

## Reviewer's two-minute tour

1. **Browse.** Type in the search box (or press <kbd>/</kbd>): matches are highlighted. Pick a category chip, switch **All · Free · Paid**, sort by popularity, date, name or price. Filters live in the URL, so a copied link reopens the same view.
2. **Launch offer.** Cards show the offer price with the M.R.P. struck through, plus **% OFF** and **Bestseller** badges. **Shop the offer** jumps to the paid templates.
3. **Save as a guest.** Tap ♥ while signed out. You are asked to sign in, and the template is saved for you right after.
4. **Sign in** with the demo account. **Details** opens a deep-linkable dialog (`/templates/:id`) with similar templates and a share button (the native share sheet on phones).
5. **Demo checkout.** In a paid template, press **Buy**: the order summary shows the M.R.P., the launch-offer saving, 18% GST and the total. **Confirm order** adds it to your library. No payment is taken and no card details are asked for. Free templates are one tap: **Get it free**.
6. **My Library** (`/library`) lists what you own with order numbers, totals paid and savings.
7. **Favorites.** Remove a template, then press **Undo** on the toast.
8. **Theme and cookies.** Switch to dark mode and reload: the choice is remembered. Dismiss the cookie notice: it stays dismissed.
9. **API.** In Swagger UI, call `POST /auth/login` with the demo account, click **Authorize**, and try the favorites and orders endpoints.

## Screenshots

| Catalogue with the launch offer (light)                     | Template details: launch price, saving, similar templates |
| ----------------------------------------------------------- | --------------------------------------------------------- |
| ![Catalogue](docs/screenshots/01-catalogue-light.png)       | ![Details](docs/screenshots/02-template-detail.png)       |
| **Demo checkout: M.R.P., saving, 18% GST, total**           | **My Library: orders, totals and savings**                |
| ![Checkout](docs/screenshots/03-demo-checkout.png)          | ![Library](docs/screenshots/04-library.png)               |
| **Search highlight + Paid filter**                          | **Checkout (dark)**                                       |
| ![Search and filter](docs/screenshots/05-search-filter.png) | ![Checkout dark](docs/screenshots/06-checkout-dark.png)   |
| **Favorites (dark)**                                        | **Sign in: inline validation + guest intent**             |
| ![Favorites](docs/screenshots/07-favorites-dark.png)        | ![Login](docs/screenshots/08-login-validation.png)        |
| **Sign up: live password strength**                         | **Link preview (WhatsApp, LinkedIn, Slack)**              |
| ![Register](docs/screenshots/09-register.png)               | ![Link preview](docs/social-preview.png)                  |

<p align="center">
  <img src="docs/screenshots/10-mobile-light.png" width="300" alt="Phone, light theme, with the launch offer and the cookie notice" />
  &nbsp;&nbsp;
  <img src="docs/screenshots/11-mobile-checkout-dark.png" width="300" alt="Phone, dark theme, demo checkout" />
</p>

## Task requirements → implementation

| Requirement (from the brief)                                           | Implementation                                                                                                                                                                          |
| ---------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| React (Vite) + Tailwind CSS                                            | React 19 + Vite 8 + **Tailwind CSS v4**, TypeScript strict                                                                                                                              |
| Node.js + Express                                                      | Express 5 + TypeScript, layered modules                                                                                                                                                 |
| Database + ORM                                                         | MongoDB Atlas + **Mongoose 9**                                                                                                                                                          |
| Register & login, JWT, hashed passwords                                | `POST /api/auth/register`, `POST /api/auth/login`, `GET /api/auth/me`; HS256 JWT; bcrypt cost 12                                                                                        |
| Templates collection: `id, name, description, thumbnail_url, category` | Exactly these fields in every response, plus `slug`, `features`, `pages`, `favorites_count`, prices (`price_inr`, `original_price_inr`, `discount_percent`, `is_free`) and `created_at` |
| `GET /api/templates`, `GET /api/templates/:id`                         | ✔ with optional `search`, `category`, `price` (`free`/`paid`), `sort` (incl. by price), `page`, `limit`                                                                                 |
| `POST /api/favorites/:templateId`, `GET /api/favorites`                | ✔ authenticated; POST is idempotent (201 first time, 200 after); plus `DELETE` to un-favourite                                                                                          |
| Seed at least 5 templates                                              | **16 templates in 6 categories** with original SVG thumbnails, seeded automatically (and `npm run seed`)                                                                                |
| Validate register/login, proper status codes + JSON                    | Strict Zod schemas for every body, query and param; consistent JSON envelope; 400/401/404/409/413/429/503                                                                               |
| Pages `/register`, `/login`, `/templates`, `/favorites`                | ✔ plus `/library`, `/templates/:id` (dialog), `/cookies` and a 404 page                                                                                                                 |
| JWT stored in localStorage                                             | ✔ (`layora:token`), with the mitigations listed under Security                                                                                                                          |
| Axios for API calls                                                    | One Axios instance with auth and error interceptors, used through TanStack Query                                                                                                        |
| Cards with a Favorite button; mark favourited templates                | ♥ toggle with `aria-pressed`, optimistic update and rollback                                                                                                                            |
| Auth redirects (favorites only for signed-in users)                    | Route guards; guests are sent to sign in and returned to where they were                                                                                                                |
| Clean, responsive UI                                                   | Soft UI Evolution design system, light/dark, 375 px → 1440 px, mobile tab bar                                                                                                           |
| **Bonus:** search / filter by category                                 | Debounced search with highlighted matches, category chips with counts, Free/Paid filter, sorts; all in the URL                                                                          |
| **Bonus:** logout + protected routes                                   | Account menu with log out; logout syncs across browser tabs                                                                                                                             |
| **Bonus:** deployment                                                  | API on Render, web app on Vercel, CI on GitHub Actions                                                                                                                                  |
| README with setup, tech stack, name & contact                          | This file, plus [client/README.md](client/README.md) and [server/README.md](server/README.md)                                                                                           |

## Beyond the brief

- **Store (demo):** INR prices with launch offers (struck-through M.R.P., **% OFF** and **Bestseller** badges, a gradient offer banner), a Free/Paid filter and price sorts, a **demo checkout** (M.R.P., launch-offer saving, 18% GST, total; no payment and no card details), idempotent orders and a **My Library** page with order numbers and savings. Prices are never taken from the browser: the server computes every amount.
- **Discovery:** similar templates in the detail dialog, highlighted search matches, and the native share sheet on phones (copy link on desktop).
- **Sample data, stated plainly:** save counts start from sample popularity (100 to 500, with two templates at 0 to show "Be the first to save") and real favourites are added on top.
- **UX:** skeleton loaders shaped like the real cards, empty and error states with recovery actions, toasts with **Undo**, a route-driven detail dialog with focus management, `/` search shortcut, keyboard-navigable category chips, mobile bottom tab bar, animated brand loader, reduced-motion support and WCAG AA contrast in both themes.
- **Theme:** light by default; the choice is saved and applied before first paint (no flash) and synced across tabs.
- **Cookie notice:** a gentle, non-blocking card (only strictly necessary storage is used) plus a `/cookies` policy page.
- **Server-waking UX:** health-check banner with backoff retries for the free-tier cold start.
- **Link previews:** Open Graph and Twitter tags with a custom 1200×630 image, so a shared link shows the logo, title and description on WhatsApp, LinkedIn and Slack.
- **API docs:** OpenAPI 3.1 with Swagger UI.
- **Quality:** 251 automated tests (214 API, 37 web), CI on every push, ESLint + Prettier + Conventional Commits enforced by git hooks.

## Tech stack

| Layer    | Choices                                                                                                                                                       |
| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Web app  | React 19, Vite 8, TypeScript, Tailwind CSS v4, shadcn/ui patterns on Radix UI, TanStack Query 5, Axios, React Router 7, React Hook Form + Zod, Sonner, Lucide |
| API      | Node.js 24, Express 5, TypeScript, Mongoose 9, Zod 4, jsonwebtoken, bcryptjs, helmet, cors, express-rate-limit, pino, Swagger UI                              |
| Database | MongoDB Atlas (production), in-memory MongoDB (local development and tests)                                                                                   |
| Testing  | Vitest, Supertest, React Testing Library, mongodb-memory-server                                                                                               |
| Tooling  | ESLint, Prettier, Husky + lint-staged, Conventional Commits, GitHub Actions                                                                                   |
| Hosting  | Vercel (web), Render (API), MongoDB Atlas                                                                                                                     |

## Architecture

## Architecture

```mermaid
flowchart LR
  subgraph Browser
    UI["React UI<br/>pages + components"] --> RQ["TanStack Query cache"]
    RQ --> AX["Axios client<br/>auth + error interceptors"]
  end
  AX -- "HTTPS JSON" --> API
  subgraph API["Express API on Render"]
    MW["helmet · CORS · rate limit · JSON 10 kB"] --> V["validate (Zod)"]
    V --> C["controller"] --> S["service"] --> M["Mongoose model"]
  end
  M --> DB[("MongoDB Atlas")]
```

- **API:** feature modules (`auth`, `templates`, `favorites`, `orders`), each split into routes → validation → controller → service → model, with serializers as response DTOs and a central error handler. See [server/README.md](server/README.md).
- **Web app:** feature folders (`auth`, `templates`, `favorites`, `orders`, `theme`), server state in TanStack Query, auth/theme in React context, URL as the source of truth for filters. See [client/README.md](client/README.md).

## Project structure

```
fullstack-intern-task/
├── client/                 React + Vite web app (deployed to Vercel)
│   ├── public/             icons, link-preview image, theme pre-paint script
│   ├── src/
│   │   ├── app/            router, query client
│   │   ├── components/     ui primitives, layout, brand, feedback states
│   │   ├── features/       auth, templates, favorites, orders, theme
│   │   ├── lib/            http client, security helpers, storage, utils
│   │   ├── pages/          cookie policy, 404
│   │   └── styles/         Tailwind v4 design tokens
│   └── vercel.json         SPA rewrites, CSP and security headers
├── server/                 Express API (deployed to Render)
│   ├── public/thumbnails/  generated SVG template thumbnails
│   ├── src/
│   │   ├── config/         validated env, MongoDB connection
│   │   ├── middlewares/    validate, authenticate, errors, security, rate limits
│   │   ├── modules/        auth, users, templates, favorites, orders, health
│   │   ├── docs/           OpenAPI 3.1 + Swagger UI
│   │   └── seed/           seed data and seeder
│   └── tests/              integration tests (Vitest + Supertest)
├── docs/                   screenshots, demo GIF and social preview
├── .github/workflows/      CI and keep-warm
├── render.yaml             Render blueprint
└── CONTRIBUTING.md         development guidelines
```

## Getting started

**Prerequisites:** Node.js 22 or newer (24 recommended, see `.nvmrc`) and npm. MongoDB is optional locally.

```bash
git clone https://github.com/Shanmugamrskfamily/fullstack-intern-task.git
cd fullstack-intern-task
npm run setup   # installs root tooling, server and client dependencies
npm run dev     # starts the API (:5000) and the web app (:5173) together
```

Then open **http://localhost:5173** and sign in with the demo account.

- **No database setup needed.** Without `MONGODB_URI`, the API starts an in-memory MongoDB and seeds 16 templates and the demo account (the first run downloads a MongoDB binary).
- **Use your own MongoDB:** copy `server/.env.example` to `server/.env` and set `MONGODB_URI`. If start-up fails with `querySrv ECONNREFUSED` (a VPN or DNS filter blocking the SRV lookup), also set `DNS_SERVERS=8.8.8.8,1.1.1.1`. Full variable lists are in [server/README.md](server/README.md#environment-variables) and [client/README.md](client/README.md#environment-variables).
- **Swagger UI:** http://localhost:5000/api/docs

| Root script         | What it does                         |
| ------------------- | ------------------------------------ |
| `npm run setup`     | Install all dependencies             |
| `npm run dev`       | Run the API and the web app together |
| `npm test`          | Run the API and web test suites      |
| `npm run lint`      | ESLint in both packages              |
| `npm run typecheck` | TypeScript checks in both packages   |
| `npm run build`     | Production builds of both packages   |

## Testing

| Suite   | Tests | Covers                                                                                                                                                                                                                                                                                                    |
| ------- | ----: | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| API     |   214 | Auth flows, every validation rule, templates search/filter/sort/pagination, prices and launch offers, idempotent favorites, demo checkout (server-side prices, races, per-user libraries), JWT tampering and expiry, NoSQL injection, rate limits, CORS, security headers, OpenAPI document               |
| Web app |    37 | Open-redirect guard, URL filter sanitising, route guards, guest favourite intent, optimistic favourites, demo checkout (guest redirect, order summary with saving and GST, focus, owned state), INR formatting, search highlighting, login validation and error mapping, theme persistence, cookie notice |

```bash
npm test
```

CI ([`.github/workflows/ci.yml`](.github/workflows/ci.yml)) runs lint, typecheck, tests and builds for both packages on every push.

## Security highlights

- **Every input validated on the server** with strict Zod schemas: unknown keys rejected, text normalised (control and zero-width characters removed), length limits and character allowlists, regex-escaped search, ObjectId checks. Mongoose `sanitizeFilter` and `strictQuery` add defence in depth.
- **Auth:** bcrypt (cost 12), JWT with the algorithm, issuer and audience pinned, timing-safe login (same response and timing for unknown emails), brute-force rate limiting, passwords never returned or logged.
- **HTTP:** helmet headers, CORS allowlist, 10 kB body limit, request IDs, no stack traces in responses. The web app ships a strict Content-Security-Policy (`script-src 'self'`), `frame-ancestors 'none'`, HSTS and related headers.
- **Checkout:** `POST /api/orders/:templateId` rejects any request body, so a price can never come from the browser; the price, saving, GST and total are computed on the server, and a unique index keeps one order per user and template.
- **Client:** React escapes all output and an ESLint rule forbids `dangerouslySetInnerHTML`; `?next=` redirects are checked against open redirects; URL parameters are cleaned and validated.
- **Honest trade-off:** the brief asks for the JWT in `localStorage`, so it is stored there. In a production product I would keep a short-lived access token in memory with an httpOnly, SameSite refresh cookie; here the strict CSP and the absence of raw-HTML rendering are what mitigate XSS.

## Design patterns

Layered architecture and feature modules (API) · middleware chain with a `validate(schema)` factory · `createApp()` factory for testable apps · singleton DB connection and Axios instance · custom error class with factory methods · response DTO serializers · provider pattern (auth, theme, query) · custom hooks as the data layer · lookup tables for sorts and price filters · route guards as layout routes · optimistic updates with rollback · interceptor pattern · query-key factory.

## Deployment

- **API → Render** (`render.yaml`): build `cd server && npm ci --include=dev && npm run build`, start `cd server && npm start`, health check `/api/health`. Secrets (`MONGODB_URI`, `JWT_SECRET`) are set in the Render dashboard, never committed. Templates and the demo account are seeded on boot (idempotent).
- **Web → Vercel** (`client/vercel.json`): root directory `client`, Vite preset, SPA rewrites, CSP and security headers. Public build-time values live in `client/.env.production`.
- **Keep warm:** `.github/workflows/keep-warm.yml` pings the API every 10 minutes.

## Development guidelines

Short-lived feature branches merged with `--no-ff`, Conventional Commits enforced by a `commit-msg` hook, ESLint and Prettier on staged files via `pre-commit`, and CI on every push. Details in [CONTRIBUTING.md](CONTRIBUTING.md).

## Author

**Shanmugam R** · Full Stack Developer (React, Next.js, Node.js) · Chennai, India

- Email: [[email protected]](mailto:[email protected])
- Phone: +91 78453 71892
- LinkedIn: [linkedin.com/in/shanmugamrskfamily](https://www.linkedin.com/in/shanmugamrskfamily)
- GitHub: [github.com/Shanmugamrskfamily](https://github.com/Shanmugamrskfamily)
- Portfolio: [shanmugam-portfolio.vercel.app](https://shanmugam-portfolio.vercel.app)

## License

[MIT](LICENSE) © 2026 Shanmugam R
反馈内容错误

看看同类模板

对比同技术栈与用途的模板,找到更适合的视觉与内容结构。