Node.Js AI Coding Rules
Node.Js AI rules help engineering teams get better results from AI coding assistants like Cursor, Windsurf, and GitHub Copilot. By defining clear conventions for code style, architecture patterns, error handling, and module organisation, Node.Js AI rules ensure that generated code is consistent, maintainable, and production-ready. Whether you are working on a side project or a large-scale enterprise system, community-curated rules on AI Rules Hub provide a solid foundation you can adopt instantly and customise to fit your team's standards.
Why Use AI Rules for Node.Js?
- Ensure AI-generated Node.Js code follows your team's conventions
- Prevent common anti-patterns that degrade maintainability
- Reduce code review cycles by getting AI output right the first time
- Standardise error handling, logging, and module structure
- Make AI assistants produce secure and performance-conscious code
Best Practices for Node.Js AI Coding
Define a Consistent Code Style
Specify formatting preferences (indentation, quotes, trailing commas) for Node.Js so AI output matches your linter configuration without manual edits.
Enforce Error Handling Patterns
Instruct AI to always handle errors explicitly, use structured logging, and avoid swallowing exceptions silently.
Set Module Organisation Rules
Define how Node.Js modules should be organised — feature folders, barrel exports, and separation of concerns — so AI keeps the project structure clean.
Require Security-Conscious Patterns
Add rules that enforce input validation, sanitisation, and safe dependency usage so AI never introduces obvious security vulnerabilities.
Common Patterns & Standards
Separation of Concerns
Keep business logic, data access, and presentation layers separate in Node.Js projects so each layer is independently testable.
Dependency Injection
Pass dependencies explicitly through constructors or function parameters — avoiding global state that makes testing difficult.
Consistent Naming Conventions
Rule AI to follow Node.Js community naming standards for files, classes, functions, and constants.
Automated Testing Standards
Define what test types are required (unit, integration) and where test files should live so AI generates tests alongside implementation code.
Top Node.Js Rules on AI Rules Hub
No description provided.
# Fastify + TypeScript + MongoDB Backend - Project Initialization Guide
> **AI Instruction**: Use this document to fully scaffold and initialize the backend project. Follow each section in order. Do not skip steps. Generate all files with the exact structure and content described.
---
## Tech Stack
| Layer | Technology |
|---|---|
| Runtime | Node.js (v20+) |
| Framework | Fastify v4 |
| Language | TypeScript (strict mode) |
| Database | MongoDB (via Mongoose v8) |
| Validation | Zod |
| Environment | dotenv |
| Dev Tooling | tsx, ts-node, eslint, prettier |
---
## Folder Structure
Generate the following folder and file structure exactly:
```
project-root/
├── src/
│ ├── config/
│ │ ├── db.ts # MongoDB connection logic
│ │ └── env.ts # Zod-validated environment variables
│ ├── modules/
│ │ └── example/
│ │ ├── example.controller.ts
│ │ ├── example.route.ts
│ │ ├── example.schema.ts # Zod schemas
│ │ ├── example.model.ts # Mongoose model
│ │ └── example.service.ts
│ ├── plugins/
│ │ ├── sensible.ts # fastify-sensible plugin
│ │ └── swagger.ts # Swagger/OpenAPI plugin (optional)
│ ├── hooks/
│ │ └── auth.hook.ts # Global auth hooks (if needed)
│ ├── utils/
│ │ ├── logger.ts # Custom logger wrapper
│ │ └── response.ts # Standard API response helpers
│ ├── types/
│ │ └── index.d.ts # Global type augmentations
│ ├── app.ts # Fastify app factory
│ └── server.ts # Entry point — starts server
├── .env
├── .env.example
├── .eslintrc.json
├── .prettierrc
├── tsconfig.json
├── package.json
└── README.md
```
---
## Step 1 — Initialize the Project
Run the following commands:
```bash
mkdir project-root && cd project-root
npm init -y
git init
```
---
## Step 2 — Install Dependencies
```bash
# Production dependencies
npm install fastify @fastify/sensible @fastify/cors mongoose zod dotenv
# Development dependencies
npm install -D typescript tsx ts-node @types/node \
eslint prettier eslint-config-prettier \
@typescript-eslint/parser @typescript-eslint/eslint-plugin
```
---
## Step 3 — TypeScript Configuration
**File: `tsconfig.json`**
```json
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"lib": ["ES2022"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
```
---
## Step 4 — Package.json Scripts
Add the following `scripts` block to `package.json`:
```json
{
"type": "module",
"scripts": {
"dev": "tsx watch src/server.ts",
"build": "tsc",
"start": "node dist/server.js",
"lint": "eslint src --ext .ts",
"format": "prettier --write src/**/*.ts"
}
}
```
---
## Step 5 — Environment Variables
**File: `.env.example`**
```env
NODE_ENV=development
PORT=3000
HOST=0.0.0.0
MONGODB_URI=mongodb://localhost:27017/mydb
JWT_SECRET=your_jwt_secret_here
```
**File: `.env`** — copy from `.env.example` and fill in real values.
---
## Step 6 — Zod Environment Validation
**File: `src/config/env.ts`**
```typescript
import { z } from 'zod';
import dotenv from 'dotenv';
dotenv.config();
const envSchema = z.object({
NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
PORT: z.coerce.number().default(3000),
HOST: z.string().default('0.0.0.0'),
MONGODB_URI: z.string().url(),
JWT_SECRET: z.string().min(16),
});
const parsed = envSchema.safeParse(process.env);
if (!parsed.success) {
console.error('❌ Invalid environment variables:');
console.error(parsed.error.flatten().fieldErrors);
process.exit(1);
}
export const env = parsed.data;
```
---
## Step 7 — MongoDB Connection
**File: `src/config/db.ts`**
```typescript
import mongoose from 'mongoose';
import { env } from './env.js';
export async function connectDB(): Promise<void> {
try {
await mongoose.connect(env.MONGODB_URI);
console.log('✅ MongoDB connected');
} catch (error) {
console.error('❌ MongoDB connection error:', error);
process.exit(1);
}
}
export async function disconnectDB(): Promise<void> {
await mongoose.disconnect();
console.log('🔌 MongoDB disconnected');
}
```
---
## Step 8 — Fastify App Factory
**File: `src/app.ts`**
```typescript
import Fastify, { FastifyInstance } from 'fastify';
import sensible from '@fastify/sensible';
import cors from '@fastify/cors';
import { exampleRoutes } from './modules/example/example.route.js';
export async function buildApp(): Promise<FastifyInstance> {
const app = Fastify({
logger: {
level: 'info',
transport: {
target: 'pino-pretty',
options: { colorize: true },
},
},
});
// Plugins
await app.register(sensible);
await app.register(cors, { origin: true });
// Routes
await app.register(exampleRoutes, { prefix: '/api/v1/examples' });
// Health check
app.get('/health', async () => ({ status: 'ok' }));
return app;
}
```
---
## Step 9 — Server Entry Point
**File: `src/server.ts`**
```typescript
import { buildApp } from './app.js';
import { connectDB, disconnectDB } from './config/db.js';
import { env } from './config/env.js';
async function main() {
const app = await buildApp();
await connectDB();
try {
await app.listen({ port: env.PORT, host: env.HOST });
console.log(`🚀 Server running at http://${env.HOST}:${env.PORT}`);
} catch (err) {
app.log.error(err);
await disconnectDB();
process.exit(1);
}
// Graceful shutdown
const shutdown = async (signal: string) => {
console.log(`\n⚠️ Received ${signal}. Shutting down...`);
await app.close();
await disconnectDB();
process.exit(0);
};
process.on('SIGINT', () => shutdown('SIGINT'));
process.on('SIGTERM', () => shutdown('SIGTERM'));
}
main();
```
---
## Step 10 — Example Module
### Zod Schema
**File: `src/modules/example/example.schema.ts`**
```typescript
import { z } from 'zod';
export const createExampleSchema = z.object({
name: z.string().min(1, 'Name is required').max(100),
description: z.string().optional(),
isActive: z.boolean().default(true),
});
export const updateExampleSchema = createExampleSchema.partial();
export const exampleParamsSchema = z.object({
id: z.string().length(24, 'Invalid MongoDB ObjectId'),
});
export type CreateExampleInput = z.infer<typeof createExampleSchema>;
export type UpdateExampleInput = z.infer<typeof updateExampleSchema>;
export type ExampleParams = z.infer<typeof exampleParamsSchema>;
```
---
### Mongoose Model
**File: `src/modules/example/example.model.ts`**
```typescript
import mongoose, { Document, Schema } from 'mongoose';
export interface IExample extends Document {
name: string;
description?: string;
isActive: boolean;
createdAt: Date;
updatedAt: Date;
}
const exampleSchema = new Schema<IExample>(
{
name: { type: String, required: true, trim: true },
description: { type: String },
isActive: { type: Boolean, default: true },
},
{
timestamps: true,
versionKey: false,
}
);
export const Example = mongoose.model<IExample>('Example', exampleSchema);
```
---
### Service Layer
**File: `src/modules/example/example.service.ts`**
```typescript
import { Example, IExample } from './example.model.js';
import { CreateExampleInput, UpdateExampleInput } from './example.schema.js';
export async function getAllExamples(): Promise<IExample[]> {
return Example.find({ isActive: true }).lean();
}
export async function getExampleById(id: string): Promise<IExample | null> {
return Example.findById(id).lean();
}
export async function createExample(data: CreateExampleInput): Promise<IExample> {
const example = new Example(data);
return example.save();
}
export async function updateExample(
id: string,
data: UpdateExampleInput
): Promise<IExample | null> {
return Example.findByIdAndUpdate(id, data, { new: true, runValidators: true }).lean();
}
export async function deleteExample(id: string): Promise<IExample | null> {
return Example.findByIdAndDelete(id).lean();
}
```
---
### Controller
**File: `src/modules/example/example.controller.ts`**
```typescript
import { FastifyRequest, FastifyReply } from 'fastify';
import { CreateExampleInput, ExampleParams, UpdateExampleInput } from './example.schema.js';
import * as service from './example.service.js';
export async function getAll(req: FastifyRequest, reply: FastifyReply) {
const data = await service.getAllExamples();
return reply.send({ success: true, data });
}
export async function getOne(
req: FastifyRequest<{ Params: ExampleParams }>,
reply: FastifyReply
) {
const item = await service.getExampleById(req.params.id);
if (!item) return reply.notFound('Example not found');
return reply.send({ success: true, data: item });
}
export async function create(
req: FastifyRequest<{ Body: CreateExampleInput }>,
reply: FastifyReply
) {
const data = await service.createExample(req.body);
return reply.status(201).send({ success: true, data });
}
export async function update(
req: FastifyRequest<{ Params: ExampleParams; Body: UpdateExampleInput }>,
reply: FastifyReply
) {
const data = await service.updateExample(req.params.id, req.body);
if (!data) return reply.notFound('Example not found');
return reply.send({ success: true, data });
}
export async function remove(
req: FastifyRequest<{ Params: ExampleParams }>,
reply: FastifyReply
) {
const data = await service.deleteExample(req.params.id);
if (!data) return reply.notFound('Example not found');
return reply.send({ success: true, message: 'Deleted successfully' });
}
```
---
### Route Registration
**File: `src/modules/example/example.route.ts`**
```typescript
import { FastifyInstance } from 'fastify';
import { zodToJsonSchema } from 'zod-to-json-schema';
import {
createExampleSchema,
updateExampleSchema,
exampleParamsSchema,
} from './example.schema.js';
import * as controller from './example.controller.js';
export async function exampleRoutes(app: FastifyInstance) {
app.get('/', { schema: { tags: ['Examples'] } }, controller.getAll);
app.get(
'/:id',
{ schema: { tags: ['Examples'], params: zodToJsonSchema(exampleParamsSchema) } },
controller.getOne
);
app.post(
'/',
{
schema: {
tags: ['Examples'],
body: zodToJsonSchema(createExampleSchema),
},
},
controller.create
);
app.put(
'/:id',
{
schema: {
tags: ['Examples'],
params: zodToJsonSchema(exampleParamsSchema),
body: zodToJsonSchema(updateExampleSchema),
},
},
controller.update
);
app.delete(
'/:id',
{ schema: { tags: ['Examples'], params: zodToJsonSchema(exampleParamsSchema) } },
controller.remove
);
}
```
> **Note**: Install `zod-to-json-schema` with: `npm install zod-to-json-schema`
---
## Step 11 — Utility Helpers
**File: `src/utils/response.ts`**
```typescript
export function successResponse<T>(data: T, message = 'Success') {
return { success: true, message, data };
}
export function errorResponse(message: string, errors?: unknown) {
return { success: false, message, errors };
}
```
---
## Step 12 — ESLint & Prettier Config
**File: `.eslintrc.json`**
```json
{
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"prettier"
],
"env": { "node": true, "es2022": true },
"rules": {
"@typescript-eslint/no-unused-vars": ["warn"],
"@typescript-eslint/explicit-function-return-type": "off"
}
}
```
**File: `.prettierrc`**
```json
{
"semi": true,
"singleQuote": true,
"trailingComma": "es5",
"printWidth": 100,
"tabWidth": 2
}
```
---
## Step 13 — .gitignore
**File: `.gitignore`**
```
node_modules/
dist/
.env
*.log
.DS_Store
```
---
## Conventions & Patterns
### Adding a New Module
To add a new module (e.g., `user`), create the following files following the same pattern:
```
src/modules/user/
├── user.controller.ts
├── user.route.ts
├── user.schema.ts
├── user.model.ts
└── user.service.ts
```
Then register the route in `src/app.ts`:
```typescript
await app.register(userRoutes, { prefix: '/api/v1/users' });
```
### Validation Pattern
Always validate input using Zod schemas in the schema file. Pass type-safe inferred types to controllers and services. Never bypass Zod for request body or params.
### Error Handling
Use `reply.notFound()`, `reply.badRequest()`, and `reply.internalServerError()` from `@fastify/sensible`. Wrap async service calls in try/catch at the controller level for unexpected errors.
---
## API Endpoints (Example Module)
| Method | Path | Description |
|---|---|---|
| GET | `/health` | Health check |
| GET | `/api/v1/examples` | List all examples |
| GET | `/api/v1/examples/:id` | Get one by ID |
| POST | `/api/v1/examples` | Create new |
| PUT | `/api/v1/examples/:id` | Update by ID |
| DELETE | `/api/v1/examples/:id` | Delete by ID |
---
## Final Checklist for AI Initialization
- [ ] Run `npm install` after generating `package.json`
- [ ] Create `.env` from `.env.example` with real values
- [ ] Ensure MongoDB is running locally or provide Atlas URI
- [ ] Run `npm run dev` to verify the server starts
- [ ] Hit `GET /health` to confirm the app is live
- [ ] Check MongoDB connection log for `✅ MongoDB connected`Production-ready PostgreSQL and Drizzle ORM performance rules for scalable SaaS applications.
# PostgreSQL + Drizzle Performance Rules for Production SaaS > Production-ready PostgreSQL and Drizzle ORM performance rules for scalable SaaS applications. --- ## Database Query Performance - Always use indexed columns in `WHERE` clauses. - Prefer composite indexes when filtering by multiple frequently used columns. - Avoid `SELECT *` in production queries. Explicitly select only required columns. - Use `EXPLAIN ANALYZE` before deploying complex queries. - Never run unbounded queries in API endpoints. --- ## Pagination & Large Data Handling - Use `LIMIT` and `OFFSET` carefully. - Prefer keyset pagination for large datasets. - Avoid loading large datasets into memory. - Always paginate API responses returning collections. --- ## Indexing Strategy - Use partial indexes for frequently filtered subsets (e.g., active records). - Create GIN indexes for full-text search (`tsvector`). - Monitor index bloat and reindex periodically. - Avoid over-indexing rarely queried columns. --- ## Drizzle ORM Best Practices - Avoid N+1 query patterns. - Prefer server-side filtering over client-side filtering. - Use transactions for multi-step writes. - Keep ORM queries predictable and optimized. - Avoid dynamic raw SQL unless necessary and always sanitize inputs. --- ## Full-Text Search Optimization - Use `tsvector` with weighted fields. - Rank results using `ts_rank`. - Create a GIN index on the search vector. - Normalize search queries using `plainto_tsquery`. --- ## Data Modeling - Use appropriate data types (`UUID` instead of `text` for IDs when possible). - Avoid excessive `JSONB` nesting. - Keep schema normalized unless denormalization is justified. - Use foreign key constraints for relational integrity. --- ## Production Safety - Use connection pooling in production environments. - Avoid blocking queries inside request lifecycles. - Implement caching for frequently accessed rule lists. - Move heavy data processing to background jobs. - Monitor slow queries using PostgreSQL logs. --- ## Observability & Monitoring - Enable query logging in staging environments. - Track performance regressions after migrations. - Audit long-running transactions. - Monitor connection pool exhaustion.
Explore Related AI Rules
Share Your Node.Js AI Rules
Have rules that improved your Node.Js workflow? Submit them to AI Rules Hub and help the community get better results from AI coding assistants.
