TypeScript AI Coding Rules
TypeScript has become the dominant language for serious JavaScript development — and AI coding assistants are only as good as the rules they follow. TypeScript AI rules define strict typing conventions, module boundaries, error handling patterns, and naming standards so that tools like Cursor, Windsurf, and GitHub Copilot generate code that is safe, maintainable, and consistent with your codebase. Whether you enforce `strict` mode, prefer `unknown` over `any`, or require exhaustive union handling, well-crafted AI rules eliminate back-and-forth corrections and keep generated code review-ready from the first draft. The community rules on AI Rules Hub cover everything from basic strictness settings to advanced patterns for generic utilities, discriminated unions, and type-safe API boundaries.
Why Use AI Rules for TypeScript?
- Prevent `any` type leakage in AI-generated code by enforcing explicit typing rules
- Ensure consistent use of `interface` vs `type` aliases across the entire codebase
- Enforce exhaustive union and enum handling so AI suggestions are bug-free
- Standardise import paths, barrel files, and module resolution patterns
- Keep generated utility types readable and reusable across projects
Best Practices for TypeScript AI Coding
Enable Strict Mode
Always enable `strict: true` in tsconfig. Add a rule instructing your AI assistant to never disable strictness flags or introduce implicit `any` types.
Prefer `unknown` Over `any`
Rule your AI to use `unknown` for external data boundaries and always narrow the type with a guard before use — preventing the most common source of runtime errors.
Name Types Clearly
Specify naming conventions for interfaces (`UserProfile`), type aliases (`UserId`), and generics (`T` for single, `TInput/TOutput` for pairs) so AI-generated types are self-documenting.
Enforce Readonly Where Applicable
Instruct your AI assistant to mark function parameters and returned collections as `readonly` by default, reducing accidental mutation bugs.
Common Patterns & Standards
Discriminated Unions for State
Model loading/error/success states as discriminated unions (`{ status: "loading" } | { status: "error"; error: Error } | { status: "success"; data: T }`) instead of boolean flags.
Zod for Runtime Validation
Pair TypeScript types with Zod schemas at system boundaries (API responses, form data) and let AI rules enforce schema-first design.
Path Aliases Over Deep Relative Imports
Use `@/` path aliases configured in tsconfig and instruct AI never to generate `../../../` style imports.
Barrel Exports With Index Files
Define module export patterns so AI knows to update `index.ts` barrel files and consume only public APIs.
Top TypeScript Rules on AI Rules Hub
No description provided.
## **Core Principles**
* Use server-first architecture; prefer server components unless client-side behavior is required.
* Maintain strict separation of concerns across UI, business logic, and data layers.
* Ensure all code is scalable, modular, and maintainable.
---
## **Data Fetching Rules**
* Do not call `fetch()` directly inside components.
* All API communication must be handled through a centralized HTTP client.
* API logic must exist only in the `services` layer.
* UI components must access data only through hooks.
---
## **Component Rules**
* Each component must have a single responsibility.
* Component files must not exceed 150 lines.
* Target component size should be between 50–80 lines.
* Large components must be split into smaller reusable components.
---
## **State Management Rules**
* Use a centralized state management solution (e.g., Zustand or Redux Toolkit).
* Do not use `useContext` for global state management.
* Keep global state minimal and domain-specific.
---
## **Forms & Validation Rules**
* All forms must use a structured form library (e.g., React Hook Form).
* All validation must be schema-based (e.g., Zod).
* Validation schemas must be reusable and stored separately.
---
## **TypeScript Rules**
* Strict TypeScript is mandatory.
* Do not use `any` type.
* All API responses must use a generic structure (e.g., `ApiResponse<T>`).
* Types must be defined and reused across the application.
---
## **Routing Rules**
* All routes must be centralized in a constants file.
* Do not hardcode route paths inside components or navigation logic.
---
## **Enums & Constants Rules**
* Do not hardcode strings such as roles, statuses, or API identifiers.
* All enums must be defined in a centralized location.
* Reuse enums across all features.
---
## **Directory Structure Rules**
* Follow feature-based architecture:
```
src/
features/
[feature]/
components/
services/
hooks/
types/
schemas/
components/ui/
store/
lib/
constants/
types/enums/
```
---
## **Layer Responsibilities**
* UI layer must handle rendering only.
* Hooks must handle business logic and data orchestration.
* Services must handle API communication.
* Store must handle global state only.
* Schemas must handle validation.
* Types must define contracts and interfaces.
---
## **Prohibited Practices**
* Do not call APIs directly inside UI components.
* Do not use `fetch()` directly.
* Do not hardcode routes or static strings.
* Do not create large monolithic components.
* Do not use `any` in TypeScript.
---
## **Architecture Philosophy**
* Build systems, not pages.
* Prioritize consistency over flexibility.
* Ensure every module is reusable and replaceable.
* Maintain clear boundaries between layers.
---
## **Compatibility**
* This rule set is designed for modern frontend stacks (React, Next.js, TanStack, etc.).
* Aligns with standardized rule-sharing ecosystems like Airuleshub .No description provided.
# Expo React Native + TypeScript Development Rules
## Project Stack
- Framework: Expo (React Native)
- Language: TypeScript
- Navigation: Expo Router or React Navigation
- State Management: Prefer React Context or Zustand
- Styling: React Native StyleSheet or Tailwind (NativeWind)
------------------------------------------------------------------------
## File Structure
src/ components/ screens/ hooks/ services/ utils/ types/ constants/
Rules: - Components must live in `components/` - Screens must live in
`screens/` - Business logic should be inside `services/` - Reusable
hooks should be inside `hooks/` - Type definitions must live in `types/`
------------------------------------------------------------------------
## TypeScript Rules
Always follow strict TypeScript rules.
Required: - Avoid `any` - Prefer `type` or `interface` - Always type
props - Always type API responses
Example:
``` ts
type User = {
id: string
name: string
email: string
}
```
Component example:
``` ts
type Props = {
title: string
}
export function Header({ title }: Props) {
return <Text>{title}</Text>
}
```
------------------------------------------------------------------------
## Component Rules
Preferred component style:
- Functional components only
- Use hooks
- Avoid class components
Example:
``` tsx
export const Button = () => {
return <Pressable></Pressable>
}
```
Rules: - One component per file - Component name must match file name
Example:
components/Button.tsx
------------------------------------------------------------------------
## Styling Rules
Preferred order:
1. StyleSheet
2. NativeWind (if enabled)
Example:
``` ts
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 16
}
})
```
Rules: - Do not inline large styles - Shared styles go in `/styles`
------------------------------------------------------------------------
## Hooks Rules
Custom hooks must start with `use`.
Example:
useAuth.ts\
useUser.ts\
useLocation.ts
Example implementation:
``` ts
export function useUser() {
const [user, setUser] = useState<User | null>(null)
return { user, setUser }
}
```
------------------------------------------------------------------------
## API Rules
All API calls must live in:
services/api/
Example:
services/api/userService.ts
Example:
``` ts
export async function getUsers(): Promise<User[]> {
const res = await fetch("/users")
return res.json()
}
```
Rules: - No API calls inside UI components - Always type API responses
------------------------------------------------------------------------
## Performance Rules
Always:
- Use React.memo for heavy components
- Use useCallback for handlers
- Use useMemo for computed values
- Avoid unnecessary re-renders
------------------------------------------------------------------------
## Expo Rules
Always prefer Expo APIs:
Examples:
expo-location\
expo-camera\
expo-secure-store\
expo-image-picker
Rules: - Do not install native packages if Expo already provides them -
Keep compatibility with Expo managed workflow
------------------------------------------------------------------------
## Error Handling
All async code must handle errors.
Example:
``` ts
try {
const users = await getUsers()
} catch (error) {
console.error(error)
}
```
------------------------------------------------------------------------
## Naming Conventions
Components → PascalCase\
Hooks → camelCase starting with `use`\
Files → PascalCase for components
Example:
UserCard.tsx\
useAuth.ts\
userService.ts
------------------------------------------------------------------------
## Testing
Preferred tools:
- Jest
- React Native Testing Library
Test files:
ComponentName.test.tsx
------------------------------------------------------------------------
## Code Quality Rules
Never:
- Use `any`
- Put business logic in UI
- Duplicate code
- Ignore TypeScript errors
Always:
- Write readable code
- Use proper types
- Split large componentsNo 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`Best practices for managing application state in React using Redux Toolkit. This rule promotes using createSlice, createAsyncThunk, typed hooks, and a feature-based structure to build scalable and ma
Use Redux Toolkit as the standard state management solution for React applications. Always create Redux logic using createSlice instead of manually writing reducers and action types. Structure Redux features using a feature-based folder structure where each slice contains its reducer, actions, and selectors. Use createAsyncThunk for handling asynchronous logic such as API calls. Keep Redux state minimal and avoid storing derived values that can be calculated from existing state. Prefer using the Redux Toolkit configureStore function to automatically enable useful middleware and dev tools. Avoid writing mutable logic in reducers unless using Redux Toolkit's Immer-powered syntax. Use typed hooks in TypeScript projects: - useAppDispatch - useAppSelector Do not store UI-only state (modals, form inputs, temporary values) in Redux unless it needs to be shared globally. Place all slices inside a dedicated "store" or "features" directory. Use selectors to access Redux state instead of directly referencing state structure in components. Ensure slices remain small and focused on a single domain or feature.
Rules to ensure scalable, performant, and maintainable Next.js applications.
You are an expert Next.js developer. Follow these rules when generating or modifying code: 1. Always prefer Server Components in Next.js unless client-side interactivity is required. 2. Use the App Router structure and organize files by feature: /app /components /lib /hooks /services 3. Avoid unnecessary client-side JavaScript. Use "use client" only when required. 4. Optimize images using next/image. 5. Use dynamic imports for heavy components to reduce bundle size. 6. Always implement loading.tsx and error.tsx for routes. 7. Follow SEO best practices using metadata API. 8. Avoid inline styles; prefer Tailwind or CSS modules. 9. Ensure accessibility (aria labels, semantic HTML). 10. Optimize performance by reducing main-thread work and avoiding heavy libraries. Always prioritize performance, readability, and scalability.
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.
These rules enforce enterprise-level type safety, maintainability, and predictable React architecture.
## 1️⃣ Strict tsconfig.json
``` json
{
"compilerOptions": {
"target": "ES2022",
"lib": ["DOM", "DOM.Iterable", "ES2022"],
"module": "ESNext",
"jsx": "react-jsx",
"strict": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
"noImplicitOverride": true,
"noFallthroughCasesInSwitch": true,
"noPropertyAccessFromIndexSignature": true,
"useUnknownInCatchVariables": true,
"forceConsistentCasingInFileNames": true,
"isolatedModules": true,
"skipLibCheck": false
}
}
```
------------------------------------------------------------------------
## 2️⃣ Never Use `any`
Use `unknown` and narrow types safely.
------------------------------------------------------------------------
## 3️⃣ Always Type Component Props
``` tsx
interface ButtonProps {
label: string;
onClick: () => void;
}
export function Button({ label, onClick }: ButtonProps): JSX.Element {
return <button onClick={onClick}>{label}</button>;
}
```
------------------------------------------------------------------------
## 4️⃣ Always Define Component Return Types
``` tsx
export function Header(): JSX.Element {
return <header>App</header>;
}
```
------------------------------------------------------------------------
## 5️⃣ Do NOT Use `React.FC`
Prefer explicit function components with typed props.
------------------------------------------------------------------------
## 6️⃣ Properly Type useState
``` tsx
interface User {
id: string;
name: string;
}
const [user, setUser] = useState<User | null>(null);
```
------------------------------------------------------------------------
## 7️⃣ Strict Event Typing
``` tsx
const handleChange = (e: React.ChangeEvent<HTMLInputElement>): void => {
setValue(e.target.value);
};
```
------------------------------------------------------------------------
## 8️⃣ Use Discriminated Unions for State
``` tsx
type FetchState =
| { status: "idle" }
| { status: "loading" }
| { status: "success"; data: string }
| { status: "error"; error: Error };
```
------------------------------------------------------------------------
## 9️⃣ Never Use Non-Null Assertion (`!`)
Always check for null explicitly.
------------------------------------------------------------------------
## 🔟 Always Type useRef
``` tsx
const inputRef = useRef<HTMLInputElement | null>(null);
```
------------------------------------------------------------------------
## 1️⃣1️⃣ Strict Context Typing
``` tsx
interface AuthContextType {
user: User | null;
login: (email: string) => Promise<void>;
}
```
------------------------------------------------------------------------
## 🏆 Golden Enterprise Rules
- ❌ No `any`
- ❌ No implicit return types
- ❌ No `React.FC`
- ❌ No non-null assertion
- ❌ No unsafe casting
- ✅ Discriminated unions
- ✅ Explicit typing everywhere
- ✅ Strict ESLint + tsconfigThis guide provides a production-ready architectural pattern for implementing TanStack Query in a React application. It focuses on scalability, type safety, and maintainability.
# The Guide to TanStack Query (React Query) in React + Vite + TypeScript
This guide provides a production-ready architectural pattern for implementing TanStack Query in a React application. It focuses on scalability, type safety, and maintainability.
## 1. Installation & Setup
First, install the core package and the devtools.
```bash
npm install @tanstack/react-query @tanstack/react-query-devtools axios
```
### Global Configuration (`src/main.tsx`)
Wrap your application in `QueryClientProvider`.
```tsx
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import App from './App';
// Create a client
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 1000 * 60 * 5, // Data is fresh for 5 minutes
gcTime: 1000 * 60 * 60, // Garbage collect unused data after 1 hour
retry: 1, // Retry failed requests once
refetchOnWindowFocus: false, // Prevent refetching on window focus (optional)
},
},
});
createRoot(document.getElementById('root')!).render(
<StrictMode>
<QueryClientProvider client={queryClient}>
<App />
{/* DevTools are essential for debugging */}
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
</StrictMode>
);
```
---
## 2. Recommended Directory Structure
Isolate your data fetching logic from your UI components.
```
src/
├── api/ # Pure Axios/Fetch functions
│ ├── axios-client.ts # configured axios instance
│ └── todos.api.ts # API methods for a specific feature
├── hooks/ # Custom React Query hooks
│ └── queries/ # Group hooks by feature
│ └── useTodos.ts
├── types/ # TypeScript interfaces
│ └── todo.types.ts
└── lib/
└── query-keys.ts # Centralized query keys
```
---
## 3. Query Key Factory Pattern (`src/lib/query-keys.ts`)
**Crucial:** Never hardcode query keys (strings/arrays) directly in your components. Use a factory to maintain consistency.
```typescript
// src/lib/query-keys.ts
export const todoKeys = {
all: ['todos'] as const,
lists: () => [...todoKeys.all, 'list'] as const,
list: (filters: string) => [...todoKeys.lists(), { filters }] as const,
details: () => [...todoKeys.all, 'detail'] as const,
detail: (id: number) => [...todoKeys.details(), id] as const,
};
```
---
## 4. The API Layer (`src/api/*`)
Keep API calls strictly as pure functions returning Promises. They should know nothing about React.
```typescript
// src/api/todos.api.ts
import axios from 'axios';
import { Todo } from '../types/todo.types';
const api = axios.create({ baseURL: 'https://jsonplaceholder.typicode.com' });
export const fetchTodos = async (): Promise<Todo[]> => {
const { data } = await api.get('/todos');
return data;
};
export const fetchTodoById = async (id: number): Promise<Todo> => {
const { data } = await api.get(`/todos/${id}`);
return data;
};
export const createTodo = async (newTodo: Omit<Todo, 'id'>): Promise<Todo> => {
const { data } = await api.post('/todos', newTodo);
return data;
};
```
---
## 5. Custom Hooks (The "Glue" Layer)
Wrap `useQuery` and `useMutation` in custom hooks. This gives you a single place to handle key management, types, and default options.
### Fetching Data (`useQuery`)
```typescript
// src/hooks/queries/useTodos.ts
import { useQuery } from '@tanstack/react-query';
import { todoKeys } from '../../lib/query-keys';
import { fetchTodos, fetchTodoById } from '../../api/todos.api';
export const useTodos = () => {
return useQuery({
queryKey: todoKeys.lists(),
queryFn: fetchTodos,
staleTime: 1000 * 60, // 1 minute specific stale time for this query
});
};
export const useTodo = (id: number) => {
return useQuery({
queryKey: todoKeys.detail(id),
queryFn: () => fetchTodoById(id),
enabled: !!id, // Only run if ID is present
});
};
```
### Mutating Data (`useMutation`)
Always invalidate related queries on success to keep the UI in sync.
```typescript
// src/hooks/queries/useTodos.ts
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { createTodo } from '../../api/todos.api';
import { todoKeys } from '../../lib/query-keys';
export const useCreateTodo = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: createTodo,
onSuccess: () => {
// Invalidate the 'lists' query so the new item appears
queryClient.invalidateQueries({ queryKey: todoKeys.lists() });
},
onError: (error) => {
console.error('Failed to create todo:', error);
},
});
};
```
---
## 6. Usage in Components
Components become clean and focused purely on UI logic.
```tsx
// src/components/TodoList.tsx
import { useTodos, useCreateTodo } from '../hooks/queries/useTodos';
export const TodoList = () => {
const { data: todos, isLoading, isError } = useTodos();
const createMutation = useCreateTodo();
if (isLoading) return <div>Loading...</div>;
if (isError) return <div>Error fetching todos</div>;
const handleCreate = () => {
createMutation.mutate({ title: 'New Task', completed: false, userId: 1 });
};
return (
<div>
<button onClick={handleCreate} disabled={createMutation.isPending}>
{createMutation.isPending ? 'Adding...' : 'Add Todo'}
</button>
<ul>
{todos?.map((todo) => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
</div>
);
};
```
---
## 7. Advanced Patterns
### Optimistic Updates
Update the UI _immediately_ before the server responds.
```typescript
export const useUpdateTodo = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: updateTodoApi,
onMutate: async (newTodo) => {
// 1. Cancel outgoing refetches
await queryClient.cancelQueries({ queryKey: todoKeys.detail(newTodo.id) });
// 2. Snapshot previous value
const previousTodo = queryClient.getQueryData(todoKeys.detail(newTodo.id));
// 3. Optimistically update
queryClient.setQueryData(todoKeys.detail(newTodo.id), (old: any) => ({
...old,
...newTodo,
}));
// 4. Return context
return { previousTodo };
},
onError: (err, newTodo, context) => {
// 5. Rollback on error
queryClient.setQueryData(todoKeys.detail(newTodo.id), context?.previousTodo);
},
onSettled: (newTodo) => {
// 6. Refetch to ensure server sync
queryClient.invalidateQueries({ queryKey: todoKeys.detail(newTodo?.id) });
},
});
};
```
### Type-Safe Error Handling
Define a global error type for your API.
```typescript
// types/api.types.ts
export interface ApiError {
message: string;
statusCode: number;
}
// In custom hook
import { useQuery, UseQueryOptions } from '@tanstack/react-query';
import { ApiError } from '../types/api.types';
export const useTodos = (options?: UseQueryOptions<Todo[], ApiError>) => {
return useQuery({
queryKey: todoKeys.lists(),
queryFn: fetchTodos,
...options,
});
};
```
---
## 8. Best Practices Checklist
- [ ] **Global Stale Time**: Set a reasonable global `staleTime` (e.g., 5 mins) to avoid excessive background fetching.
- [ ] **Query Key Factories**: Always use a factory/object for keys (`todoKeys.list(filter)`).
- [ ] **Separation of Concerns**: `Component` -> `Custom Hook` -> `API Function`.
- [ ] **Invalidation**: Always invalidate parent lists when creating/deleting items.
- [ ] **DevTools**: Keep `ReactQueryDevtools` in your app wrapper (it's stripped in production).No description provided.
You are an expert Chrome extension developer, proficient in JavaScript/TypeScript, browser extension APIs, and web development. Code Style and Structure - Write clear, modular TypeScript code with proper type definitions - Follow functional programming patterns; avoid classes - Use descriptive variable names (e.g., isLoading, hasPermission) - Structure files logically: popup, background, content scripts, utils - Implement proper error handling and logging - Document code with JSDoc comments Architecture and Best Practices - Strictly follow Manifest V3 specifications - Divide responsibilities between background, content scripts and popup - Configure permissions following the principle of least privilege - Use modern build tools (webpack/vite) for development - Implement proper version control and change management Chrome API Usage - Use chrome.* APIs correctly (storage, tabs, runtime, etc.) - Handle asynchronous operations with Promises - Use Service Worker for background scripts (MV3 requirement) - Implement chrome.alarms for scheduled tasks - Use chrome.action API for browser actions - Handle offline functionality gracefully Security and Privacy - Implement Content Security Policy (CSP) - Handle user data securely - Prevent XSS and injection attacks - Use secure messaging between components - Handle cross-origin requests safely - Implement secure data encryption - Follow web_accessible_resources best practices Performance and Optimization - Minimize resource usage and avoid memory leaks - Optimize background script performance - Implement proper caching mechanisms - Handle asynchronous operations efficiently - Monitor and optimize CPU/memory usage UI and User Experience - Follow Material Design guidelines - Implement responsive popup windows - Provide clear user feedback - Support keyboard navigation - Ensure proper loading states - Add appropriate animations Internationalization - Use chrome.i18n API for translations - Follow _locales structure - Support RTL languages - Handle regional formats Accessibility - Implement ARIA labels - Ensure sufficient color contrast - Support screen readers - Add keyboard shortcuts Testing and Debugging - Use Chrome DevTools effectively - Write unit and integration tests - Test cross-browser compatibility - Monitor performance metrics - Handle error scenarios Publishing and Maintenance - Prepare store listings and screenshots - Write clear privacy policies - Implement update mechanisms - Handle user feedback - Maintain documentation Follow Official Documentation - Refer to Chrome Extension documentation - Stay updated with Manifest V3 changes - Follow Chrome Web Store guidelines - Monitor Chrome platform updates Output Expectations - Provide clear, working code examples - Include necessary error handling - Follow security best practices - Ensure cross-browser compatibility - Write maintainable and scalable code
Explore Related AI Rules
Share Your TypeScript AI Rules
Have rules that improved your TypeScript workflow? Submit them to AI Rules Hub and help the community get better results from AI coding assistants.
