

Also from Kynth Studios


Also from Kynth Studios


Also from Kynth Studios
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- Return consistent HTTP responses8788Example route implementation:89```ts90const moduleRoutes = new Hono()91 .use(auth(),requireAuth)92 .get("/", async (c) => {93 const items = await moduleService.getItems();94 return c.json(items);95 })96 .post("/", zValidator("json", insertSchema), async (c) => {97 const data = c.req.valid("json");98 const userId = getUserId(c);99 const result = await moduleService.createItem({ ...data, userId });100 // If results could be undefined or otherwise, we can throw http101 if (!result){102 // throw proper error code103 throw new HTTPException(404, {message: ...})104 }105 return c.json(result);106 });107```108109After the route is created, you must add it to the `apps/api/src/index.ts` route so its accessable by the frontend.110111</module_development_guidelines>112113<frontend_integration>114When fetching data from the backend api on the client, use the following guidelines115`post.api.ts`116```ts117import { apiRpc, getApiClient, InferRequestType, callRpc } from "./client";118119const $createPost = apiRpc.posts.$post;120// Simple get121export async function getPosts() {122 const client = await getApiClient();123124 return callRpc(client.posts.$get());125}126// Safely leverage the typed params elsewhere within the nextjs application127export type CreatePostParams = InferRequestType<typeof $createPost>["json"];128export async function createPost(params: CreatePostParams) {129 const client = await getApiClient();130131 return callRpc(client.posts.$post({ json: params }));132}133```134</frontend_integration>135<package_management>136137- Use `pnpm` as the primary package manager for the project138- Install dependencies using `pnpm add [package-name]`139- Install dev dependencies using `pnpm add -D [package-name]`140- Install workspace dependencies using `pnpm add -w [package-name]`141</package_management>142143<development_guidelines>144### Running Scripts145- Use `bun` as the runtime environment and script runner146- Execute scripts defined in package.json using `bun run [script-name]`147- Run TypeScript files directly using `bun [file.ts]`148149### Monorepo150The project use turbo repo. To run everything, use the `turbo dev` script from the root folder.151152### Type Safety153- Use Drizzle schemas for database types154- Share types between frontend and backend using a shared package155- Leverage zod for runtime validation156157### Error Handling158- Implement consistent error handlers159- Use proper HTTP status codes160- Return structured error responses161- Handle edge cases appropriately162- Use the logger from the package @repo/logger with object inside, i.e `logger.error({...})` instead of `console.error`163164### Authentication & Authorization165- Use Clerk middleware for authentication166- Implement role-based access control where needed167- Validate user permissions at the route level168- Keep authentication logic in middleware169170### API Design Principles171- Follow RESTful conventions172- Use consistent naming patterns173- Implement proper request validation174- Structure endpoints by resource/module175- Keep routes clean and delegate logic to services176177### Database Operations178- Use Drizzle for all database interactions179- Implement proper migrations180- Handle transactions when needed181- Write efficient queries182- Use appropriate indexes183</development_guidelines>184185<dev_workflow>186### Creating a New Module for different buisness logic1871. Create module directory in api/src/modules/[module]1882. Define routes in [module].routes.ts1893. Implement service logic in [module].service.ts1904. Add route to main application in index.ts1915. Create frontend integration in web/src/api/[module].api.ts192</dev_workflow>193194### Testing Requirements195- If needed use vitest to create testing files inside of the modules, [module].test.ts196-197### Code Quality198- For methods with more than one argument, use object destructuring: `function myMethod({ param1, param2 }: MyMethodParams) {...}`.199</best_practices>200201202<example_api_workflow>203Here's a complete example of a typical module:204205(if required) create new schemas.206// packages/db/schema.ts207.. new schema files208209Then:210```ts211// posts.service.ts212import { db, posts, type NewPost } from "@repo/db";213214export const postService = {215 async getPosts() {216 return db.select().from(posts);217 },218219 async createPost(post: NewPost) {220 return db.insert(posts).values(post).returning();221 }222};223224// posts.routes.ts225import { Hono } from "hono";226import { auth, requireAuth, getUserId } from "@/pkg/middleware/clerk-auth";227import { postService } from "./post.service";228import { zValidator } from "@/pkg/util/validator-wrapper";229import { postInsertSchema } from "@repo/db";230231export const postRoutes = new Hono()232 .use(auth(),requireAuth)233 .get("/", async (c) => {234 const posts = await postService.getPosts();235 return c.json(posts);236 })237 .post("/", zValidator("json", postInsertSchema), async (c) => {238 const data = c.req.valid("json");239 const userId = getUserId(c);240 const post = await postService.createPost({ ...data, userId });241 return c.json(post);242 });243```244245- Use the RPC-style API client for type-safe API calls246- Leverage `InferRequestType` for parameter typing247- Export plain async functions for API operations248- Optionally create React Query hooks when needed249```ts250// posts.api.ts (Frontend)251import { apiRpc, getApiClient, InferRequestType, callRpc } from "./client";252253const $createPost = apiRpc.posts.$post;254255export async function getPosts() {256 const client = await getApiClient();257 return callRpc(client.posts.$get());258}259260export async function createPost(params: InferRequestType<typeof $createPost>["json"]) {261 const client = await getApiClient();262 return callRpc(client.posts.$post({ json: params }));263 return response.json();264}265```266</example_api_workflow>267268269
One 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 |
|---|---|---|---|---|---|
| Allymahmoud/case-intake-platform.cursor/rules/db.mdc · 0 | Cursor rules | no sections | 44/100 | 14 days ago | |
| Allymahmoud/case-intake-platform.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 14 days ago |
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 | 14 days ago | |
| TechSquidTV/Hermes.cursor/rules/10-hermes-api.mdc · 46 | Cursor rules | testlint-formatstylearch+5 | 100/100 | 14 days ago | |
| markstev/mark-starter.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+6 | 99/100 | 14 days ago | |
| deifos/clipmira-subtitles.cursor/rules/frontend.mdc · 1 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 14 days ago | |
| dodgecfr/combatfilms-webapp.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 14 days ago | |
| Allymahmoud/case-intake-platform.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 14 days ago | |
| langflow-ai/langflow.cursor/rules/docs_development.mdc · 153k | Cursor rules | setupbuildtestlint-format+7 | 97/100 | 14 days ago | |
| TechSquidTV/Hermes.cursor/rules/20-hermes-api-tests.mdc · 46 | Cursor rules | teststyletesting-strategysecurity+3 | 97/100 | 14 days ago |
A badge carrying the measured quality of the strongest agent config file in this repository, out of 100. It reads from this index every time somebody loads your page, so it changes when the measurement changes and there is nothing to keep up to date. Free, no account, and the value is not something you or we can set by hand.
[](https://rulestack.kynth.studio/configs/allymahmoud-case-intake-platform-cursor-rules-api)Would rather not hotlink us? Every badge is also served in shields.io’s endpoint schema, so shields renders the image and your readers never talk to our domain:
Published by Toolproof, the masthead over this index and eight others. The method behind the number is at toolproof.kynth.studio/methodology, and the whole thing is readable as JSON with no key at /api.