Cursor rule
.cursor/rules/api.mdc[object Object]
Cursor rules
Quality
78/100
Scores the file, not the repository.Length
1,222 words
15 headings · 7 code blocksRepository
29
— · pushed 46 days agoLast changed
3 days ago
First indexed 3 days ago.123456# Backend API Architecture and Development Guidelines78You are an expert TypeScript backend engineer specializing in building modern, type-safe APIs. Your expertise covers Hono for HTTP routing, Drizzle ORM for database operations, and React Query for frontend integration.910<core_architecture>1112<tech_stack>13- **Server & Routing**: Hono14- **Database ORM**: Drizzle with PostgreSQL15- **Frontend Integration**: React Query16- **Authentication**: Clerk17- **Validation**: Zod with zValidator18</tech_stack>1920<project_structure>21apps/22 ├── api/23 │ ├── src/24 │ │ ├── modules/ # Feature-based modules25 │ │ │ ├── [module]/ # e.g., posts, webhooks26 │ │ │ │ ├── [module].routes.ts # Route definitions27 │ │ │ │ └── [module].service.ts # Business logic & DB operations28 │ │ ├── pkg/ # Shared utilities and middleware29 │ │ └── index.ts # Main application setup30 └── web/31 └── src/32 └── api/33 └── [module].api.ts # React Query hooks3435packages/36 └── db/37 ├── src/38 │ ├── schema.ts # Database schema definitions39 │ ├── types.ts # Shared TypeScript types40 │ ├── index.ts # Main exports41 │ └── util/ # Database utilities42</project_structure>4344<module_development_guidelines>45### 1. Database layer46- If the request needs a new column or addition database fields start by creating and updating schema.ts47- Leverage types.ts inside packages/db to create new zod schemas for the newly created tables or columns and eg:48```49export type Post = InferSelectModel<typeof schema.posts>;50export type NewPost = InferInsertModel<typeof schema.posts>;5152export const postInsertSchema = createInsertSchema(schema.posts).omit({ userId: true });53export const postSelectSchema = createSelectSchema(schema.posts);54```555657### 2. Service Layer ([module].service.ts)58- Implement business logic59- Handle database operations using Drizzle60- Return strongly typed responses61- Keep services focused and modular62- Import db from the `packages/db`6364Example service implementation:65```ts66`[module].service.ts`67import { db, eq, items} from "@repo/db"6869export const moduleService = {70 async getItems() {71 return db.select().from(items);72 },7374 async createItem(data: NewItem) {75 return db.insert(items).values(data).returning();76 }77};78```798081### 3. Route Layer ([module].routes.ts)82- Define endpoints using Hono router83- Implement request validation using zValidator. You can leverage the created zod schemas from drizzle zod.84- Apply authentication middleware where needed85- Structure routes logically by resource86- **Use custom error classes instead of manual status responses** - Import from `@/pkg/errors` and throw appropriate errors87- **Avoid wrapping route handlers in try-catch blocks** - let error middleware handle exceptions88- Delegate all business logic and error handling to the service layer8990Example route implementation:91```ts92import { NotFoundError } from "@/pkg/errors";9394const moduleRoutes = new Hono()95 .use(auth(),requireAuth)96 .get("/", async (c) => {97 const items = await moduleService.getItems();98 return c.json(items);99 })100 .post("/", zValidator("json", insertSchema), async (c) => {101 const data = c.req.valid("json");102 const userId = getUserId(c);103 const result = await moduleService.createItem({ ...data, userId });104 // If results could be undefined or otherwise, throw proper error105 if (!result){106 throw new NotFoundError("Item not found");107 }108 return c.json(result);109 });110111112```113114After the route is created, you must add it to the `apps/api/src/index.ts` route so its accessable by the frontend.115116</module_development_guidelines>117118<frontend_integration>119When fetching data from the backend api on the client, use the following guidelines120`post.api.ts`121```ts122import { apiRpc, getApiClient, InferRequestType, callRpc } from "./client";123124const $createPost = apiRpc.posts.$post;125// Simple get126export async function getPosts() {127 const client = await getApiClient();128129 return callRpc(client.posts.$get());130}131// Safely leverage the typed params elsewhere within the nextjs application132export type CreatePostParams = InferRequestType<typeof $createPost>["json"];133export async function createPost(params: CreatePostParams) {134 const client = await getApiClient();135136 return callRpc(client.posts.$post({ json: params }));137}138```139</frontend_integration>140<package_management>141142- Use `pnpm` as the primary package manager for the project143- Install dependencies using `pnpm add [package-name]`144- Install dev dependencies using `pnpm add -D [package-name]`145- Install workspace dependencies using `pnpm add -w [package-name]`146</package_management>147148<development_guidelines>149### Running Scripts150- Use `bun` as the runtime environment and script runner151- Execute scripts defined in package.json using `bun run [script-name]`152- Run TypeScript files directly using `bun [file.ts]`153154### Monorepo155The project use turbo repo. To run everything, use the `turbo dev` script from the root folder.156157### Type Safety158- Use Drizzle schemas for database types159- Share types between frontend and backend using a shared package160- Leverage zod for runtime validation161162### Error Handling163- **Use custom error classes instead of manual JSON responses** - Import and throw errors from `@/pkg/errors/error`164- Available error classes: `NotFoundError`, `BadRequestError`, `UnauthorizedError`, `ForbiddenError`, `ConflictError`, `UnprocessableEntityError`, `InternalServerError`, `TooManyRequestsError`165- **NEVER return manual status responses** like `return c.json({error: "message"}, 401)` - always throw proper error classes166- Implement consistent error handlers across all routes167- Handle edge cases by throwing appropriate error classes168- Use the logger from the package @repo/logger with object inside, i.e `logger.error({...})` instead of `console.error`169170#### Error Class Usage Examples:171```ts172// ❌ DON'T DO THIS173if (!user) {174 return c.json({ error: "User not found" }, 404);175}176177// ✅ DO THIS INSTEAD178import { NotFoundError, BadRequestError, ConflictError } from "@/pkg/errors";179180if (!user) {181 throw new NotFoundError("User not found");182}183184if (email && await userExists(email)) {185 throw new ConflictError("User with this email already exists");186}187188if (!isValidInput(data)) {189 throw new BadRequestError("Invalid input data provided");190}191```192193### Authentication & Authorization194- Use Clerk middleware for authentication195- Implement role-based access control where needed196- Validate user permissions at the route level197- Keep authentication logic in middleware198199### API Design Principles200- Follow RESTful conventions201- Use consistent naming patterns202- Implement proper request validation203- Structure endpoints by resource/module204- Keep routes clean and delegate logic to services205206### Database Operations207- Use Drizzle for all database interactions208- Implement proper migrations209- Handle transactions when needed210- Write efficient queries211- Use appropriate indexes212</development_guidelines>213214<dev_workflow>215### Creating a New Module for different buisness logic2161. Create module directory in api/src/modules/[module]2172. Define routes in [module].routes.ts2183. Implement service logic in [module].service.ts2194. Add route to main application in index.ts2205. Create frontend integration in web/src/api/[module].api.ts221</dev_workflow>222223### Testing Requirements224- If needed use vitest to create testing files inside of the modules, [module].test.ts225-226### Code Quality227- For methods with more than one argument, use object destructuring: `function myMethod({ param1, param2 }: MyMethodParams) {...}`.228</best_practices>229230231<example_api_workflow>232Here's a complete example of a typical module:233234(if required) create new schemas.235// packages/db/schema.ts236.. new schema files237238Then:239```ts240// posts.service.ts241import { db, posts, type NewPost } from "@repo/db";242243export const postService = {244 async getPosts() {245 return db.select().from(posts);246 },247248 async createPost(post: NewPost) {249 return db.insert(posts).values(post).returning();250 }251};252253// posts.routes.ts254import { Hono } from "hono";255import { auth, requireAuth, getUserId } from "@/pkg/middleware/clerk-auth";256import { postService } from "./post.service";257import { zValidator } from "@/pkg/util/validator-wrapper";258import { postInsertSchema } from "@repo/db";259260export const postRoutes = new Hono()261 .use(auth(),requireAuth)262 .get("/", async (c) => {263 const posts = await postService.getPosts();264 return c.json(posts);265 })266 .post("/", zValidator("json", postInsertSchema), async (c) => {267 const data = c.req.valid("json");268 const userId = getUserId(c);269 const post = await postService.createPost({ ...data, userId });270 return c.json(post);271 });272```273274- Use the RPC-style API client for type-safe API calls275- Leverage `InferRequestType` for parameter typing276- Export plain async functions for API operations277- Optionally create React Query hooks when needed278```ts279// posts.api.ts (Frontend)280import { apiRpc, getApiClient, InferRequestType, callRpc } from "./client";281282const $createPost = apiRpc.posts.$post;283284export async function getPosts() {285 const client = await getApiClient();286 return callRpc(client.posts.$get());287}288289export async function createPost(params: InferRequestType<typeof $createPost>["json"]) {290 const client = await getApiClient();291 return callRpc(client.posts.$post({ json: params }));292 return response.json();293}294```295</example_api_workflow>296297298
Also in AsharibAli/ramadan-prompting-nights
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 |
|---|---|---|---|---|---|
| AsharibAli/ramadan-prompting-nights.cursor/rules/db.mdc · 29 | Cursor rules | no sections | 44/100 | 3 days ago | |
| AsharibAli/ramadan-prompting-nights.cursor/rules/frontend.mdc · 29 | Cursor rules | setuptestlint-formatstyle+7 | 91/100 | 3 days ago |
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 |
