No description provided.
## π― RULE: ROUTING + FEATURES MUST BE SEPARATED * `app/` β routing only * `features/` β business logic --- ## π FOLDER STRUCTURE (NEXT.JS) ``` src/ β βββ app/ # Next.js App Router β βββ layout.tsx β βββ globals.css β β βββ (public)/ β β βββ layout.tsx β β βββ page.tsx # landing β β βββ login/ β β βββ page.tsx β β βββ (protected)/ β β βββ layout.tsx β β βββ dashboard/ β β βββ page.tsx β β βββ (admin)/ β β βββ admin/ β β βββ page.tsx β β βββ api/ # route handlers β βββ auth/ β βββ route.ts β βββ features/ β βββ auth/ β β βββ components/ β β β βββ LoginForm.tsx β β β βββ RegisterForm.tsx β β β β β βββ hooks/ β β β βββ useLogin.ts β β β βββ useSession.ts β β β β β βββ queries/ β β β βββ auth.queries.ts β β β βββ auth.mutations.ts β β β β β βββ services/ β β β βββ auth.api.ts β β β β β βββ types/ β β β βββ auth.types.ts β β β β β βββ utils/ β β β βββ auth.utils.ts β β β β β βββ constants/ β β β βββ auth.constants.ts β β β β β βββ index.ts β β β βββ rules/ β β βββ components/ β β βββ hooks/ β β βββ queries/ β β βββ services/ β β βββ types/ β β βββ utils/ β β βββ constants/ β β βββ index.ts β βββ shared/ β βββ components/ β βββ hooks/ β βββ lib/ β β βββ api.ts β β βββ query-client.ts β β βββ auth.ts β βββ utils/ β βββ constants/ β βββ types/ β βββ providers/ β βββ QueryProvider.tsx β βββ middleware.ts # auth / route guards β βββ styles/ β βββ globals.css β βββ config/ β βββ env.ts β βββ types/ β βββ next.config.js ``` --- ## β‘ NEXT.JS RULES * `app/` MUST NOT contain business logic * All logic MUST live inside `features/` * Server components MUST NOT include client-side logic unnecessarily * TanStack Query MUST be used for client-side server state * API routes MUST remain thin (no heavy logic) * Middleware MUST handle authentication and routing guards --- ## π« COMMON FORBIDDEN * API calls inside components * Cross-feature imports * Global business logic * useEffect for fetching * Monolithic folders
No description provided.
## π― RULE: FEATURE-BASED STRUCTURE IS MANDATORY All code MUST be organized by feature. Each feature MUST be self-contained and isolated. --- ## π FOLDER STRUCTURE (REACT) ``` src/ β βββ app/ # App entry (providers, routing setup) β βββ App.tsx β βββ providers.tsx β βββ routes.tsx β βββ features/ β βββ auth/ β β βββ components/ β β β βββ LoginForm.tsx β β β βββ RegisterForm.tsx β β β β β βββ hooks/ β β β βββ useLogin.ts β β β βββ useRegister.ts β β β β β βββ queries/ β β β βββ auth.queries.ts β β β βββ auth.mutations.ts β β β β β βββ services/ β β β βββ auth.api.ts β β β β β βββ types/ β β β βββ auth.types.ts β β β β β βββ utils/ β β β βββ auth.utils.ts β β β β β βββ constants/ β β β βββ auth.constants.ts β β β β β βββ index.ts β β β βββ products/ β β βββ components/ β β βββ hooks/ β β βββ queries/ β β βββ services/ β β βββ types/ β β βββ utils/ β β βββ constants/ β β βββ index.ts β βββ shared/ β βββ components/ # reusable UI (Button, Modal) β βββ hooks/ # generic hooks β βββ lib/ # api client, query client β β βββ api.ts β β βββ query-client.ts β βββ utils/ β βββ constants/ β βββ types/ β βββ providers/ β βββ QueryProvider.tsx # TanStack setup β βββ styles/ β βββ globals.css β βββ assets/ β βββ images/ β βββ icons/ β βββ config/ β βββ env.ts β βββ types/ # global types (rare) β βββ main.tsx ``` --- ## β‘ REACT RULES * Features MUST NOT import from other features * Components MUST NOT call APIs directly * All server state MUST use TanStack Query * Hooks MUST encapsulate all business logic * Shared layer MUST remain generic (no domain logic) * Query keys MUST be structured and typed --- ## π« COMMON FORBIDDEN * API calls inside components * Cross-feature imports * Global business logic * useEffect for fetching * Monolithic folders
Defines strict, scalable architecture rules for React and Next.js using feature-based design and TanStack Query.
These rules define **mandatory architecture, data flow, and code organization** for scalable React / Next.js applications using:
* Next.js (App Router or TanStack Start equivalent)
* TanStack Query
* TypeScript
* Feature-based architecture
These rules MUST be followed by:
* Developers
* AI code generators
* Code reviewers
---
## π§± ARCHITECTURE RULES
### RULE: FEATURE-BASED STRUCTURE IS MANDATORY
All code MUST be organized by feature.
* Each domain MUST have its own isolated module
* Cross-feature coupling is FORBIDDEN
Allowed:
* features/auth
* features/rules
* features/profile
Forbidden:
* global components folder for business logic
* shared services containing feature logic
---
### RULE: FEATURES MUST BE SELF-CONTAINED
Each feature MUST include:
* components
* hooks
* services
* queries
* types
A feature MUST NOT depend on another featureβs internal files.
---
### RULE: SHARED LAYER IS STRICTLY LIMITED
The shared layer MAY contain ONLY:
* UI primitives
* generic hooks
* utilities
* API client
The shared layer MUST NOT contain:
* business logic
* feature-specific rules
* domain-specific APIs
---
## π DATA FLOW RULES
### RULE: SERVER STATE MUST USE TANSTACK QUERY
All server data MUST be handled via TanStack Query.
Forbidden:
* fetch inside useEffect
* direct API calls in components
---
### RULE: QUERY KEYS MUST BE STRUCTURED
Query keys MUST include parameters.
Allowed:
* ['rules', { page, filter }]
* ['profile', userId]
Forbidden:
* ['rules']
* ['data']
---
### RULE: MUTATIONS MUST INVALIDATE QUERIES
Every mutation MUST define cache invalidation.
Example requirement:
* create β invalidate list
* update β invalidate detail + list
---
### RULE: NO DUPLICATE DATA SOURCES
A single source of truth MUST exist per data entity.
Forbidden:
* storing API data in local state AND query cache
* syncing multiple stores manually
---
## π API LAYER RULES
### RULE: API CALLS MUST BE ISOLATED
All API logic MUST exist inside:
* feature/services
Components MUST NEVER call APIs directly.
---
### RULE: API FUNCTIONS MUST BE PURE
API functions MUST:
* return data only
* contain no UI logic
* contain no React hooks
---
### RULE: CENTRALIZED API CLIENT IS REQUIRED
A single API client MUST handle:
* authentication headers
* error handling
* base URL
---
## π§© COMPONENT RULES
### RULE: COMPONENTS MUST BE PURE
UI components MUST:
* receive data via props
* NOT fetch data
* NOT contain business logic
---
### RULE: CONTAINER LOGIC MUST USE HOOKS
All logic MUST be extracted into hooks.
Components MUST NOT:
* manage server state
* implement side effects
---
### RULE: COMPONENT SIZE LIMIT
Components MUST NOT exceed:
* 200 lines (soft limit)
* 300 lines (hard limit β refactor required)
---
## π§ HOOK RULES
### RULE: ALL DATA ACCESS MUST GO THROUGH HOOKS
Hooks MUST wrap:
* queries
* mutations
* derived state
---
### RULE: HOOK NAMING IS STRICT
Format:
* use<Feature><Action>
Examples:
* useRules
* useCreateRule
* useUserProfile
---
### RULE: HOOKS MUST BE REUSABLE
Hooks MUST NOT depend on:
* specific UI components
* DOM structure
---
## ποΈ ROUTING RULES
### RULE: ROUTES MUST BE GROUPED BY ACCESS
Routing MUST follow:
* public routes
* authenticated routes
* admin routes
As seen in system architecture
---
### RULE: ROUTES MUST NOT CONTAIN BUSINESS LOGIC
Routes MAY:
* compose components
* call hooks
Routes MUST NOT:
* fetch data directly
* implement API logic
---
## π AUTHENTICATION RULES
### RULE: AUTH MUST BE HANDLED GLOBALLY
Authentication MUST be:
* managed via middleware or provider
* injected into API client
---
### RULE: TOKEN HANDLING MUST BE SECURE
Preferred:
* HTTP-only cookies
Forbidden:
* storing sensitive tokens in localStorage (unless unavoidable)
---
## β‘ PERFORMANCE RULES
### RULE: UNNECESSARY RE-RENDERS MUST BE AVOIDED
Use:
* select in queries
* memoization where needed
---
### RULE: CONDITIONAL FETCHING MUST USE QUERY OPTIONS
Use:
* enabled flag
Forbidden:
* conditional hooks
---
### RULE: LAZY LOADING IS REQUIRED FOR SCALING
Large features MUST be lazy-loaded.
---
## π§ͺ TESTING RULES
### RULE: BUSINESS LOGIC MUST BE TESTED
Test:
* hooks
* services
---
### RULE: UI TESTING IS SECONDARY
Avoid:
* testing implementation details
* fragile DOM tests
---
## π’ SCALABILITY RULES
### RULE: FEATURES MUST SCALE INDEPENDENTLY
Each feature MUST:
* be removable without breaking system
* be deployable in isolation (future-ready)
---
### RULE: NO TIGHT COUPLING
Forbidden:
* importing internal files across features
* shared mutable state across domains
---
### RULE: INDEX EXPORTS ARE REQUIRED
Each feature MUST expose:
* public API via index.ts
---
## π« FORBIDDEN PATTERNS
The following are STRICTLY NOT ALLOWED:
* API calls inside components
* useEffect for data fetching
* global state for server data
* monolithic folders
* cross-feature imports
* hardcoded query keys
* mixed concerns (UI + logic)
---
## π€ AI GENERATION RULES
AI MUST:
* generate feature-based structure
* create hooks for all data access
* use TanStack Query for server state
* separate API layer
* generate TypeScript types
* avoid inline logic in components
AI MUST NOT:
* create global services for feature logic
* fetch data inside components
* mix concerns
---
## β
FINAL RULE
If code violates separation of concerns, it MUST be rejected.
If data is server-derived, it MUST use TanStack Query.
If logic is reusable, it MUST be extracted into hooks.
---
## π OUTCOME
Following these rules guarantees:
* predictable architecture
* scalability across teams
* AI-compatible codebases
* clean separation of concerns
* production-ready frontend systemsNo 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.
# Pure State β Zustand Patterns That Actually Scale
> **AI Instruction**: This document defines the rules, conventions, and architecture decisions for implementing Zustand state management in a React + TypeScript project. Do not deviate from these patterns. When generating stores, hooks, or components β always refer back to these rules first.
---
## Stack
- **Zustand v4+** β primary state management
- **TypeScript strict mode** β all stores must be fully typed
- **Immer middleware** β for any nested state mutations
- **DevTools middleware** β applied to every store, always
- **Persist middleware** β only for state that must survive page refresh
---
## Folder Structure
```
src/
βββ stores/
β βββ index.ts # Re-export all stores + resetAllStores()
β βββ useAuthStore.ts
β βββ useUIStore.ts
β βββ useUserStore.ts
β βββ slices/ # Only for large, multi-concern stores
β βββ types.ts
β βββ authSlice.ts
β βββ cartSlice.ts
βββ hooks/
β βββ useStoreSelectors.ts # All memoized selectors live here
βββ types/
βββ store.d.ts # Shared store type declarations
```
---
## Store Architecture Rules
### One Store Per Domain
Every store must own exactly one concern. Never mix auth logic with UI state, or user profile with cart data. If two pieces of state are unrelated, they belong in separate stores. The store name must clearly reflect what it owns β `useAuthStore`, `useCartStore`, `useUIStore`.
### Always Define an Initial State Object
Every store must declare a separate `initialState` const above the store definition. This object is what the `reset()` action returns to. It also documents at a glance what shape the store holds without reading the full type definition.
### Every Store Must Have a Reset Action
Without exception, every store must implement a `reset()` action that restores the store to its `initialState`. This is called on logout, session expiry, or any global teardown. A store without `reset()` is incomplete.
### Always Apply DevTools Middleware
Every store β no matter how small β must be wrapped with `devtools()` middleware. The store name must be passed as the `name` option. Every `set()` call must include a descriptive action name as the third argument so that DevTools traces are meaningful and debuggable.
### Actions Belong Inside the Store
No business logic or state mutation should live outside the store. Components call store actions β they never call `set()` directly or manage async themselves. If a component is doing `setState` based on API data, that logic needs to move into a store action.
---
## TypeScript Conventions
### Always Separate State from Actions in the Interface
Define the store interface with a clear visual separation β state fields first, then actions. Use comments to divide them. This makes the interface scannable and prevents confusion between what is data and what is callable.
### Use Strict Typing for All State Fields
Never use `any`. Every field must have an explicit type. Optional fields must be typed as `T | null` β never as `T | undefined` unless the field is genuinely optional in a form or partial update context. Null signals "not yet loaded". Undefined signals "doesn't exist".
### Infer Action Parameter Types from Zod Schemas When Available
If the project uses Zod for API validation, action input types should be inferred from those schemas using `z.infer<typeof schema>`. Never duplicate type definitions between Zod schemas and store interfaces.
---
## Async Action Rules
### Every Async Action Must Follow the Three-Phase Pattern
When an async action starts, immediately set `isLoading: true` and `error: null`. On success, set the result data and `isLoading: false`. On failure, set the error message and `isLoading: false`. No async action is complete without all three phases handled.
### Always Name Async Action States in DevTools
When calling `set()` inside an async action, name each phase clearly β for example `login/pending`, `login/fulfilled`, `login/rejected`. This mirrors Redux Toolkit conventions and makes DevTools traces readable.
### Never Let Errors Silently Fail
Every catch block must write to the store's `error` field. The error message must be a human-readable string. Always check if the caught value is an instance of `Error` before accessing `.message` β otherwise fall back to a generic string.
### Access Current State Inside Async Actions with `get()`
When an async action needs to read current state mid-execution β for example to attach a token to a request β use `get()` from the store creator. Never close over state values from outside the action, as they may be stale.
### Never Trigger Actions from Inside Other Actions
Actions must not call sibling actions directly. If two actions share logic, extract that logic into a private utility function inside the store file and call it from both. Cross-action dependencies cause unpredictable execution order.
---
## Selector Rules
### Never Subscribe to the Whole Store
Components must never destructure the full store object. Subscribing to the whole store causes the component to re-render on every single state change regardless of relevance. Always pass a selector function that picks exactly the value the component needs.
### All Selectors Live in `useStoreSelectors.ts`
Every named selector β `useIsAuthenticated`, `useCurrentUser`, `useAuthLoading` β must be defined in the dedicated selectors file and exported from there. Components import from this file, not directly from the store. This centralizes all selector logic and makes refactoring easier.
### Actions Are Safe to Destructure Together
Action functions are stable references and do not cause re-renders. It is acceptable to select a group of actions together in one selector. State values must always be selected individually.
### Use Shallow Comparison for Object Selections
If a selector must return an object with multiple fields, use `shallow` from Zustand as the equality function. Without this, the component re-renders even when the object's values haven't changed because a new object reference is returned on every call.
---
## Immer Middleware Rules
### Use Immer Only When State is Nested Two or More Levels Deep
For flat state with primitive values, use regular `set()`. Apply Immer middleware to stores where deeply nested objects need to be updated β user profiles with nested preferences, addresses, or settings. Immer is not needed for simple top-level assignments.
### Never Return and Mutate in the Same Immer Callback
Inside an Immer `set()` callback, either mutate the draft directly or return a new object β never both. Mixing the two causes Immer to throw. Choose mutation style for nested updates, return style for complete state replacements.
### Always Guard Against Null Before Mutating Nested Objects
Before mutating a nested object inside an Immer callback, always check that the parent exists. If the parent is null and the action tries to mutate a child property, Immer will throw a runtime error. Defensive null checks are mandatory.
---
## Persist Middleware Rules
### Only Persist What Is Truly Necessary
Never persist entire store state by default. Always use `partialize` to explicitly whitelist fields that should survive a page refresh. Persisting loading states, error messages, or transient UI values is a bug.
### Always Name the Storage Key Explicitly
The `name` field in persist config is the localStorage key. It must be explicit, unique, and human-readable β for example `auth-storage` or `theme-preferences`. Never use auto-generated or ambiguous names.
### Persist Tokens, Not Sensitive Data
Authentication tokens may be persisted in localStorage for session continuity. Passwords, raw API responses, or personally identifiable information must never be persisted. When in doubt, do not persist.
---
## Slice Pattern Rules
### Use Slices Only for Large Multi-Domain Stores
The slice pattern β combining multiple `StateCreator` functions into one bound store β is appropriate when a single store legitimately crosses domains, such as a checkout flow that needs both cart state and auth state together. For typical apps, separate stores are preferred over slices.
### Define All Slice Types in a Shared Types File
Every slice interface must be defined in `slices/types.ts`. The combined `AppStore` type that merges all slice interfaces also lives there. Individual slice files import from types β they never redefine their own interfaces inline.
### Each Slice Is Responsible Only for Its Own State
A slice action must never directly mutate another slice's state. If coordination is needed between slices, it must happen at the bound store level through composition, not by cross-referencing slice internals.
---
## Reset and Cleanup Rules
### Export a `resetAllStores()` Function from `stores/index.ts`
The `index.ts` file must export a single `resetAllStores()` function that calls `.getState().reset()` on every store. This function is called in exactly one place β the logout handler. It is never called from components directly.
### Subscriptions Created Outside React Must Be Unsubscribed
If `store.subscribe()` is used outside a React component β for example to attach an auth token to an API client β the returned unsubscribe function must be stored and called during application teardown. Leaking subscriptions causes stale callbacks.
---
## Component Integration Rules
### Components Are Consumers Only
A component's only job with respect to state is to read values via selectors and call actions on user interaction. Components must not derive new state, filter arrays, or compute totals inline. That logic belongs in the store or in a dedicated selector.
### Co-locate Loading and Error State with Their Data
Whenever a component renders async data, it must also handle the corresponding `isLoading` and `error` states from the same store. Rendering data without handling its loading and error states is incomplete.
### Clear Errors Explicitly
After an async action fails and the error is displayed, always provide a mechanism β a dismiss button, a timeout, or a navigation event β to call `clearError()`. Errors must not persist in state indefinitely.
---
## Anti-Patterns β Never Do These
| β Never | β
Always |
|---|---|
| Subscribe to the full store object | Select only the value needed |
| Put async logic inside components | Move async into store actions |
| Mutate state directly | Use `set()` or Immer |
| Create a store without DevTools | Every store gets `devtools()` |
| Skip loading or error state | All three async phases are required |
| Mix UI state and domain state | Separate stores per concern |
| Persist loading, errors, or UI flags | Only persist tokens and preferences |
| Call `reset()` from components | Use `resetAllStores()` from logout only |
| Duplicate types already in Zod schemas | Infer with `z.infer<>` |
| Hardcode localStorage key strings | Define as named constants |
---
## Final Checklist for AI Initialization
- [ ] One store file created per domain concern
- [ ] Every store has `initialState` const defined above the store
- [ ] Every store has a `reset()` action returning `initialState`
- [ ] Every store is wrapped with `devtools()` middleware and named
- [ ] All async actions handle `pending`, `fulfilled`, and `rejected` phases
- [ ] All selectors are defined in `useStoreSelectors.ts`
- [ ] No component subscribes to the full store object
- [ ] `resetAllStores()` is exported from `stores/index.ts`
- [ ] Persist middleware uses `partialize` to whitelist only necessary fields
- [ ] Immer middleware applied only to stores with nested state
- [ ] All store interfaces separate state fields from action fields
- [ ] No async logic lives inside React componentsNo description provided.
# Next.js SSR Project Development Rules
These rules define the architecture and coding standards for this
Next.js project.
Supported AI IDEs: - Cursor - Windsurf - GitHub Copilot - VS Code AI
tools - JetBrains AI Assistant
Framework: Next.js (App Router) Language: TypeScript Rendering: Server
Components + SSR
------------------------------------------------------------------------
# Core Principles
Always prioritize:
1. Server Components
2. SSR data fetching
3. Type safety
4. Separation of concerns
5. Scalable folder structure
Avoid unnecessary client components.
------------------------------------------------------------------------
# Project Folder Structure
Use the following structure.
app/ (api)/ (auth)/ dashboard/ layout.tsx page.tsx
components/ ui/ forms/ shared/
services/ api/ server/
lib/ db/ auth/ config/
hooks/ types/ utils/ constants/ styles/
Rules: - UI components β components/ - API communication β
services/api - Server logic β services/server - Shared utilities β
utils - Types β types
------------------------------------------------------------------------
# Rendering Rules
Default behavior must be Server Components.
Example:
``` tsx
export default async function Page() {
const users = await getUsers()
return (
<div>
{users.map(user => (
<p key={user.id}>{user.name}</p>
))}
</div>
)
}
```
Only use client components when needed.
Client component must start with:
"use client"
------------------------------------------------------------------------
# SSR Data Fetching Rules
Always fetch data on the server.
Example:
``` ts
async function getUsers() {
const res = await fetch("https://api.example.com/users", {
cache: "no-store"
})
return res.json()
}
```
Rules: - Prefer fetch() inside server components - Use revalidate for
caching - Avoid client-side API calls unless required
------------------------------------------------------------------------
# Server Actions Rules
Use Server Actions for mutations.
Example:
``` ts
"use server"
export async function createUser(formData: FormData) {
const name = formData.get("name")
await db.user.create({
data: { name }
})
}
```
Rules: - All mutations must be server actions - Never expose secrets to
client components
------------------------------------------------------------------------
# API Route Rules
Location:
app/api/
Example:
app/api/users/route.ts
Example API handler:
``` ts
export async function GET() {
return Response.json({ success: true })
}
```
Rules: - API routes should only contain request handling - Business
logic must live in services
------------------------------------------------------------------------
# Component Rules
Component guidelines: - Functional components only - Keep components
small - Separate UI and logic
Example:
components/UserCard.tsx
Example:
``` tsx
type Props = {
name: string
}
export function UserCard({ name }: Props) {
return <div>{name}</div>
}
```
------------------------------------------------------------------------
# TypeScript Rules
Strict TypeScript must be used.
Never use: any
Always type: - Props - API responses - Services
Example:
``` ts
type User = {
id: string
name: string
email: string
}
```
------------------------------------------------------------------------
# Service Layer Rules
All API logic must live in services.
Example structure:
services/ api/ userService.ts authService.ts
Example:
``` ts
export async function getUsers(): Promise<User[]> {
const res = await fetch("/api/users")
return res.json()
}
```
Rules: - UI components must not call APIs directly - Use service
functions
------------------------------------------------------------------------
# SEO Rules
Always use Next.js metadata API.
Example:
``` ts
export const metadata = {
title: "Dashboard",
description: "User dashboard"
}
```
------------------------------------------------------------------------
# Performance Rules
Always use built-in Next.js optimizations.
Required tools: - next/image - next/font - dynamic imports
Example:
``` tsx
import Image from "next/image"
```
------------------------------------------------------------------------
# Environment Variables
Secrets must live in:
.env.local
Example:
DATABASE_URL= NEXT_PUBLIC_API_URL= JWT_SECRET=
Rules: - Server secrets must never be exposed to client components
------------------------------------------------------------------------
# Error Handling
Use proper error boundaries.
Example:
app/error.tsx
Example:
``` tsx
export default function Error({ error }) {
return <div>Something went wrong</div>
}
```
------------------------------------------------------------------------
# Naming Conventions
Components β PascalCase Hooks β camelCase starting with use Services β
camelCase
Examples: UserCard.tsx useAuth.ts userService.ts formatPrice.ts
------------------------------------------------------------------------
# Testing Rules
Testing tools: - Jest - React Testing Library
Example test file:
UserCard.test.tsx
------------------------------------------------------------------------
# Code Quality Rules
Never: - Use any - Put business logic inside UI - Fetch data inside
client components unnecessarily - Duplicate logic
Always: - Write reusable components - Use proper types - Follow folder
structure - Keep components smallNo 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.
Best practices to improve web performance and Lighthouse score.
When writing frontend code, follow these performance rules: 1. Reduce JavaScript bundle size. 2. Lazy load components when possible. 3. Use code splitting. 4. Avoid unnecessary state updates. 5. Debounce expensive operations. 6. Optimize images and use modern formats (WebP). 7. Avoid blocking scripts. 8. Use memoization for heavy computations. 9. Reduce DOM nodes where possible. 10. Ensure Lighthouse performance score stays above 90.
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
