{One-sentence description of what this project does.} Stack: MongoDB, Express {4.x}, React {18/19}, Node.js {20.x}, TypeScript Package manager: {npm/pnpm} | Monorepo: {yes/no}
# Server (Express API)
{cd server && npm run dev} # Nodemon dev server (port 4000)
{cd server && npm run build} # TypeScript compile
{cd server && npm test} # Jest tests
# Client (React)
{cd client && npm run dev} # Vite dev server (port 5173)
{cd client && npm run build} # Production build
{cd client && npm test} # Vitest tests
# Both (from root)
{npm run dev} # Concurrently start server + client
{npm run test:all} # Run all tests
# Database
{npm run db:seed} # Seed MongoDB with sample dataserver/
├── src/
│ ├── app.ts # Express app setup, middleware
│ ├── server.ts # Entry point, DB connection, listen
│ ├── config/ # Environment config, constants
│ ├── routes/ # Express routers (one per resource)
│ ├── controllers/ # Request handlers (called by routes)
│ ├── services/ # Business logic (called by controllers)
│ ├── models/ # Mongoose schemas and models
│ ├── middleware/ # Auth, validation, error handler
│ ├── types/ # Shared TypeScript types
│ └── utils/ # Helpers (logger, email, tokens)
│
client/
├── src/
│ ├── components/ # React components
│ │ ├── ui/ # Generic: Button, Modal, Input
│ │ └── features/ # Domain: UserProfile, PostCard
│ ├── pages/ # Route-level page components
│ ├── hooks/ # Custom React hooks
│ ├── services/ # API client functions (axios calls)
│ ├── stores/ # {Zustand/Context} state management
│ ├── types/ # Client TypeScript types
│ └── utils/ # Client helpers
- RESTful:
GET/POST/PUT/PATCH/DELETE /api/v1/{resource} - Route -> Controller -> Service -> Model (strict layer separation)
- Validation: {Zod/Joi} schemas in middleware, validated before controller
- Error responses:
{ success: false, error: { code, message } } - Success responses:
{ success: true, data: {...}, meta?: {pagination} } - Pagination:
?page=1&limit=20— default limit 20, max 100
// Standard route pattern
router.get('/', validate(listUsersSchema), userController.list);
router.post('/', validate(createUserSchema), userController.create);
router.get('/:id', userController.getById);
router.patch('/:id', validate(updateUserSchema), userController.update);
router.delete('/:id', userController.delete);- Strategy: {JWT access + refresh tokens / session-based}
- Access token: short-lived ({15 min}), sent in
Authorization: Bearer <token> - Refresh token: long-lived ({7 days}), httpOnly cookie
- Middleware:
authMiddlewareverifies token, attachesreq.user - Password hashing: bcrypt with salt rounds {12}
- Protected routes:
router.use(authMiddleware)on router group
- Define schema and model in the same file in
server/src/models/ - Use TypeScript interface for document type
- Timestamps: always enable
{ timestamps: true } - Indexes: define in schema for frequent query fields
- Virtuals for computed fields, pre-hooks for hashing/sanitization
- Never expose
__vor sensitive fields — usetoJSONtransform
// Standard Mongoose model
const userSchema = new Schema<IUser>({
email: { type: String, required: true, unique: true, lowercase: true },
password: { type: String, required: true, select: false },
role: { type: String, enum: ['user', 'admin'], default: 'user' },
}, { timestamps: true });
userSchema.pre('save', async function() { /* hash password */ });
userSchema.methods.comparePassword = async function(candidate: string) { /* ... */ };
export const User = model<IUser>('User', userSchema);- Functional components with
constarrow syntax, named exports - API calls in
client/src/services/via axios instance with interceptors - Auth state in context/store — token refresh handled by axios interceptor
- Styling: {Tailwind CSS} utility classes
- Forms: {React Hook Form} + {Zod} for validation
- Routing: {React Router v6} with protected route wrapper
- TypeScript strict mode on both server and client
- Named exports only (except Mongoose models)
interfacefor object shapes,typefor unions- No
any— useunknownand narrow - Server logging via structured logger — no
console.login production
- Do not put business logic in controllers — delegate to services
- Do not query MongoDB directly in routes or controllers — use service/model layer
- Do not store JWT secrets or DB credentials in code — use env vars
- Do not install new dependencies without asking first
- Do not modify shared types without checking both server and client usage