Cursor rule
.cursor/rules/services.mdcService layer architecture, API patterns, and data management with TanStack Query
Cursor rules
Quality
73/100
Scores the file, not the repository.Length
546 words
12 headings · 7 code blocksRepository
63
— · pushed 15 days agoLast changed
3 days ago
First indexed 3 days ago.1234567# Service Layer & Data Management Rules89## Service Layer Organization1011### File Structure12```13src/lib/14├── utils.ts # General utilities (keep cn function)15├── types.ts # Shared TypeScript interfaces16├── constants.ts # App-wide constants17├── validations/ # Zod schemas18│ ├── user.ts19│ ├── product.ts20│ └── common.ts21└── services/ # API service layer22 ├── api.ts # Base API client23 ├── userService.ts24 └── productService.ts25```2627### Service Layer Pattern28```tsx29// services/userService.ts30export const userService = {31 async getAll(): Promise<User[]> {32 const response = await fetch('/api/users')33 if (!response.ok) throw new Error('Failed to fetch users')34 return response.json()35 },3637 async getById(id: string): Promise<User> {38 const response = await fetch(`/api/users/${id}`)39 if (!response.ok) throw new Error('Failed to fetch user')40 return response.json()41 },4243 async create(data: CreateUserData): Promise<User> {44 const response = await fetch('/api/users', {45 method: 'POST',46 headers: { 'Content-Type': 'application/json' },47 body: JSON.stringify(data)48 })49 if (!response.ok) throw new Error('Failed to create user')50 return response.json()51 },5253 async update(id: string, data: Partial<User>): Promise<User> {54 const response = await fetch(`/api/users/${id}`, {55 method: 'PATCH',56 headers: { 'Content-Type': 'application/json' },57 body: JSON.stringify(data)58 })59 if (!response.ok) throw new Error('Failed to update user')60 return response.json()61 }62}63```6465### Shared Types (lib/types.ts)66```tsx67export interface User {68 id: string69 email: string70 name: string71 avatar?: string72 role: UserRole73 createdAt: string74 updatedAt: string75}7677export interface ApiResponse<T> {78 data: T79 message: string80 success: boolean81 meta?: {82 total: number83 page: number84 limit: number85 }86}8788export type UserRole = 'admin' | 'user' | 'guest'89export type Theme = 'light' | 'dark' | 'system'90export type LoadingState = 'idle' | 'loading' | 'success' | 'error'91```9293### Constants (lib/constants.ts)94```tsx95export const API_ENDPOINTS = {96 USERS: '/api/users',97 PRODUCTS: '/api/products',98 AUTH: '/api/auth',99} as const100101export const QUERY_KEYS = {102 USERS: 'users',103 PRODUCTS: 'products',104 USER_PROFILE: 'user-profile',105} as const106107export const ROUTES = {108 HOME: '/',109 DASHBOARD: '/dashboard',110 USERS: '/users',111 PROFILE: '/profile',112} as const113```114115### Validation Schemas116```tsx117// lib/validations/common.ts118export const emailSchema = z.string().email('Invalid email address')119export const phoneSchema = z.string().regex(/^\+?[\d\s-()]+$/, 'Invalid phone number')120121// lib/validations/user.ts122export const userSchema = z.object({123 name: z.string().min(2, 'Name must be at least 2 characters'),124 email: emailSchema,125 phone: phoneSchema.optional(),126 role: z.enum(['admin', 'user', 'guest'])127})128129export const createUserSchema = userSchema.omit({ role: true })130export const updateUserSchema = userSchema.partial()131132export type UserFormData = z.infer<typeof userSchema>133export type CreateUserData = z.infer<typeof createUserSchema>134export type UpdateUserData = z.infer<typeof updateUserSchema>135```136137## TanStack Query Integration138139### Query Hooks Pattern140```tsx141// hooks/useUsers.ts142export const useUsers = (params?: UserQueryParams) => {143 return useQuery({144 queryKey: [QUERY_KEYS.USERS, params],145 queryFn: () => userService.getAll(params),146 staleTime: 5 * 60 * 1000, // 5 minutes147 })148}149150export const useUser = (id: string) => {151 return useQuery({152 queryKey: [QUERY_KEYS.USERS, id],153 queryFn: () => userService.getById(id),154 enabled: !!id,155 })156}157```158159### Mutation Hooks Pattern160```tsx161// hooks/useUserMutations.ts162export const useCreateUser = () => {163 const queryClient = useQueryClient()164165 return useMutation({166 mutationFn: userService.create,167 onSuccess: () => {168 queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.USERS] })169 toast.success('User created successfully')170 },171 onError: (error) => {172 toast.error(error.message)173 }174 })175}176```177178## NEVER in Components179- Direct API calls (`fetch`, `axios` inline)180- Business logic mixed with UI181- Hardcoded API URLs182- Duplicate validation schemas183184## ALWAYS Use185- Service layer for all API calls186- Shared types for consistent interfaces187- Custom hooks for data fetching188- Centralized constants for URLs and keys189
Also in chihebnabil/lovable-boilerplate
Diff this repo’s formatsOne repository carrying more than one format is the comparison this product exists for: does anyone actually write different content in each file, or is one a copy of the other?
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| chihebnabil/lovable-boilerplate.cursor/rules/design.mdc · 63 | Cursor rules | styleuido-not | 65/100 | 3 days ago | |
| chihebnabil/lovable-boilerplate.cursor/rules/forms.mdc · 63 | Cursor rules | setupstyledo-not | 73/100 | 3 days ago | |
| chihebnabil/lovable-boilerplate.cursor/rules/hooks.mdc · 63 | Cursor rules | styleuido-not | 61/100 | 3 days ago | |
| chihebnabil/lovable-boilerplate.cursor/rules/typescript.mdc · 63 | Cursor rules | setupstyletypessecurity+2 | 73/100 | 3 days ago | |
| chihebnabil/lovable-boilerplate.github/instructions/architecture.instructions.md · 63 | Copilot instructions | stylearchtypesdatabase+2 | 69/100 | 3 days ago | |
| chihebnabil/lovable-boilerplate.github/instructions/design.instructions.md · 63 | Copilot instructions | lint-formatstyleuido-not | 61/100 | 3 days ago | |
| chihebnabil/lovable-boilerplate.github/instructions/development.instructions.md · 63 | Copilot instructions | setupbuildtestlint-format+7 | 88/100 | 3 days ago | |
| chihebnabil/lovable-boilerplate.github/instructions/hooks.instructions.md · 63 | Copilot instructions | styleui | 54/100 | 3 days ago | |
| chihebnabil/lovable-boilerplate.github/instructions/lib.instructions.md · 63 | Copilot instructions | archtypesdo-not | 69/100 | 3 days ago | |
| chihebnabil/lovable-boilerplate.github/instructions/pages.instructions.md · 63 | Copilot instructions | archuido-not | 69/100 | 3 days ago | |
| chihebnabil/lovable-boilerplate.github/instructions/quality.instructions.md · 63 | Copilot instructions | lint-formatdeploymentdo-not | 63/100 | 3 days ago | |
| chihebnabil/lovable-boilerplate.github/instructions/reusable.instructions.md · 63 | Copilot instructions | uido-notagent-behaviour | 32/100 | 3 days ago | |
| chihebnabil/lovable-boilerplateCLAUDE.md · 63 | CLAUDE.md | setupbuildtestlint-format+5 | 89/100 | 3 days ago | |
| chihebnabil/lovable-boilerplate.cursor/rules/core.mdc · 63 | Cursor rules | buildlint-formatarchui+1 | 92/100 | 3 days ago | |
| chihebnabil/lovable-boilerplate.github/instructions/components.instructions.md · 63 | Copilot instructions | styleuido-not | 61/100 | 3 days ago | |
| chihebnabil/lovable-boilerplate.github/instructions/global.instructions.md · 63 | Copilot instructions | buildlint-formatstylearch+4 | 100/100 | 3 days ago | |
| chihebnabil/lovable-boilerplate.cursor/rules/components.mdc · 63 | Cursor rules | stylearchuido-not | 65/100 | 3 days ago | |
| chihebnabil/lovable-boilerplate.cursor/rules/quality.mdc · 63 | Cursor rules | buildlint-formatstyleui+3 | 88/100 | 3 days ago |
Diff against .cursor/rules/design.mdc Diff against .cursor/rules/forms.mdc Diff against .cursor/rules/hooks.mdc Diff against .cursor/rules/typescript.mdc Diff against .github/instructions/architecture.instructions.md Diff against .github/instructions/design.instructions.md Diff against .github/instructions/development.instructions.md Diff against .github/instructions/hooks.instructions.md Diff against .github/instructions/lib.instructions.md Diff against .github/instructions/pages.instructions.md Diff against .github/instructions/quality.instructions.md Diff against .github/instructions/reusable.instructions.md Diff against CLAUDE.md Diff against .cursor/rules/core.mdc Diff against .github/instructions/components.instructions.md Diff against .github/instructions/global.instructions.md Diff against .cursor/rules/components.mdc Diff against .cursor/rules/quality.mdc
Similar configs
Same format, overlapping stack, ranked by quality.
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| hiromaily/go-crypto-wallet.cursor/rules/typescript.mdc · 126 | Cursor rules | setupbuildtestlint-format+6 | 100/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/10-hermes-api.mdc · 45 | Cursor rules | testlint-formatstylearch+5 | 100/100 | 3 days ago | |
| markstev/mark-starter.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+6 | 99/100 | 3 days ago | |
| Allymahmoud/case-intake-platform.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 3 days ago | |
| dodgecfr/combatfilms-webapp.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 3 days ago | |
| deifos/clipmira-subtitles.cursor/rules/frontend.mdc · 1 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 3 days ago | |
| langflow-ai/langflow.cursor/rules/docs_development.mdc · 153k | Cursor rules | setupbuildtestlint-format+7 | 97/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/20-hermes-api-tests.mdc · 45 | Cursor rules | teststyletesting-strategysecurity+3 | 97/100 | 3 days ago |
