

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 return c.json(result);101 });102```103104After the route is created, you must add it to the `apps/api/src/index.ts` route so its accessable by the frontend.105106</module_development_guidelines>107108<frontend_integration>109When fetching data from the backend api on the client, use the following guidelines110`post.api.ts`111```ts112import { apiRpc, getApiClient, InferRequestType } from "./client";113114const $createPost = apiRpc.posts.$post;115// Simple get116export async function getPosts() {117 const client = await getApiClient();118119 const response = await client.posts.$get();120 return response.json();121}122// Safely leverage the typed params elsewhere within the nextjs application123export type CreatePostParams = InferRequestType<typeof $createPost>["json"];124export async function createPost(params: CreatePostParams) {125 const client = await getApiClient();126127 const response = await client.posts.$post({ json: params });128 return response.json();129}130```131</frontend_integration>132<package_management>133134- Use `pnpm` as the primary package manager for the project135- Install dependencies using `pnpm add [package-name]`136- Install dev dependencies using `pnpm add -D [package-name]`137- Install workspace dependencies using `pnpm add -w [package-name]`138</package_management>139140<development_guidelines>141### Running Scripts142- Use `bun` as the runtime environment and script runner143- Execute scripts defined in package.json using `bun run [script-name]`144- Run TypeScript files directly using `bun [file.ts]`145146### Monorepo147The project use turbo repo. To run everything, use the `turbo dev` script from the root folder.148149### Type Safety150- Use Drizzle schemas for database types151- Share types between frontend and backend using a shared package152- Leverage zod for runtime validation153154### Error Handling155- Implement consistent error handlers156- Use proper HTTP status codes157- Return structured error responses158- Handle edge cases appropriately159- Use the logger from the package @repo/logger, i.e `logger.error` instead of `console.error`160161### Authentication & Authorization162- Use Clerk middleware for authentication163- Implement role-based access control where needed164- Validate user permissions at the route level165- Keep authentication logic in middleware166167### API Design Principles168- Follow RESTful conventions169- Use consistent naming patterns170- Implement proper request validation171- Structure endpoints by resource/module172- Keep routes clean and delegate logic to services173174### Database Operations175- Use Drizzle for all database interactions176- Implement proper migrations177- Handle transactions when needed178- Write efficient queries179- Use appropriate indexes180</development_guidelines>181182<dev_workflow>183### Creating a New Module for different buisness logic1841. Create module directory in api/src/modules/[module]1852. Define routes in [module].routes.ts1863. Implement service logic in [module].service.ts1874. Add route to main application in index.ts1885. Create frontend integration in web/src/api/[module].api.ts189</dev_workflow>190191### Testing Requirements192- If needed use vitest to create testing files inside of the modules, [module].test.ts193-194### Code Quality195- For methods with more than one argument, use object destructuring: `function myMethod({ param1, param2 }: MyMethodParams) {...}`.196</best_practices>197198199<example_api_workflow>200Here's a complete example of a typical module:201202(if required) create new schemas.203// packages/db/schema.ts204.. new schema files205206Then:207```ts208// posts.service.ts209import { db, posts, type NewPost } from "@repo/db";210211export const postService = {212 async getPosts() {213 return db.select().from(posts);214 },215216 async createPost(post: NewPost) {217 return db.insert(posts).values(post).returning();218 }219};220221// posts.routes.ts222import { Hono } from "hono";223import { auth, requireAuth, getUserId } from "@/pkg/middleware/clerk-auth";224import { postService } from "./post.service";225import { zValidator } from "@/pkg/util/validator-wrapper";226import { postInsertSchema } from "@repo/db";227228export const postRoutes = new Hono()229 .use(auth(),requireAuth)230 .get("/", async (c) => {231 const posts = await postService.getPosts();232 return c.json(posts);233 })234 .post("/", zValidator("json", postInsertSchema), async (c) => {235 const data = c.req.valid("json");236 const userId = getUserId(c);237 const post = await postService.createPost({ ...data, userId });238 return c.json(post);239 });240```241242- Use the RPC-style API client for type-safe API calls243- Leverage `InferRequestType` for parameter typing244- Export plain async functions for API operations245- Optionally create React Query hooks when needed246```ts247// posts.api.ts (Frontend)248import { apiRpc, getApiClient, InferRequestType } from "./client";249250const $createPost = apiRpc.posts.$post;251252export async function getPosts() {253 const client = await getApiClient();254 const response = await client.posts.$get();255 return response.json();256}257258export async function createPost(params: InferRequestType<typeof $createPost>["json"]) {259 const client = await getApiClient();260 const response = await client.posts.$post({ json: params });261 return response.json();262}263```264265Please note, on the frontend, do not need to generate response interfaces. The rpc client will handle safely types responses.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 |
|---|---|---|---|---|---|
| dodgecfr/combatfilms-webapp.cursor/rules/db.mdc · 0 | Cursor rules | no sections | 44/100 | 14 days ago | |
| dodgecfr/combatfilms-webapp.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/dodgecfr-combatfilms-webapp-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.