[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6C2D7XWDG24MYSNQKP80P0V":3,"skill-same:01M6C2D7XWDG24MYSNQKP80P0V":53},{"skillNo":4,"name":5,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":22,"publishedAt":26,"linkOnly":27,"markdownContent":28,"markdownBody":28,"frontMatter":29,"refName":30,"commitSha":31,"skillDirectory":32,"forksCount":33,"repositoryHtmlUrl":34,"repositoryHomepageUrl":35,"ownerHtmlUrl":36,"ownerBlogUrl":6,"ownerDescription":37,"updatedAt":38,"install":39,"resources":52},"01M6C2D7XWDG24MYSNQKP80P0V","backend-controller",null,"davidleonmayor\u002Fproyecto-grado-unimayor","davidleonmayor","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F142275316?v=4","01M6C2D07NAZV2VQE96MD4HWYW","David León","01M6C2D0BN278C193FYXQB3TME",3,"MIT","https:\u002F\u002Fgithub.com\u002Fdavidleonmayor\u002Fproyecto-grado-unimayor\u002Fblob\u002Fmain\u002Fskills\u002Fbackend-controller\u002FSKILL.md",[17],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000010","后端与接口","SCENARIO","使用场景",[23,24,25],"controller","backend","backend controller","2026-11-10 10:12:30",false,"# Backend Controller\n\n## Overview\nThis skill defines the convention for backend controller classes. Controllers should be **thin** - their only job is to parse the request, call the appropriate service, and format the response. All validation and auth logic belongs elsewhere.\n\n## Trigger\nWhen creating or updating controller classes for backend features (e.g., `auth.controller.ts`, `messaging.controller.ts`, `project.controller.ts`).\n\n## Do's\n\n- **Thin Controller**: Controllers should only handle HTTP concerns (parse request, call service, format response). All business logic goes in the Service layer.\n- **Use Services**: Inject and use Service classes to perform business operations. Do NOT call `prisma` directly from controllers (except for simple read-only queries that are tightly coupled to the HTTP response).\n- **Extract Body Data**: Destructure `req.body` to get input parameters.\n- **Access Auth User**: Use `req.user` directly (after middleware has validated auth). Do NOT check if `req.user` exists - that's the middleware's job.\n- **Return Proper HTTP Status Codes**: Use correct status codes (201 for created, 200 for ok, 401 for unauthorized, 404 for not found, 409 for conflict, 500 for errors).\n- **Handle Errors**: Wrap controller logic in try-catch. Return user-friendly error messages. Use `next(error)` for unexpected errors to let the global error handler deal with them.\n- **Type Safety**: Use proper TypeScript types for `Request`, `Response`, and custom interface types. Avoid `(req as any)`.\n- **Use Custom Request Interface**: Define a custom interface that extends Express's Request to include custom properties like `user`.\n\n## Don'ts\n\n- **Don't Validate Data Types\u002FLengths\u002FFormats**: That's the Schema's job (using `express-validator`). If a field is missing or invalid, the schema validation middleware will reject it before the controller runs.\n- **Don't Check Authentication**: Do NOT check `if (!req.user)` or `if (!req.headers.authorization)`. That's the AuthMiddleware's job. If the request reached the controller, the user is authenticated.\n- **Don't Check Authorization Roles**: Do NOT check `if (req.user.role === 'admin')`. That's the RoleMiddleware's job.\n- **Don't Use `console.log\u002Ferror`**: Use the `logger` from `config\u002F` or simply omit logging in controllers (services should handle logging).\n- **Don't Call Prisma Directly**: Except for simple queries tightly coupled to the response. Use Services for business logic.\n- **Don't Re-validate Business Rules That Are Already Validated**: Don't check `if (!email)` or `if (password.length \u003C 8)` - the schema already did this.\n- **Don't Mix Responsibilities**: A controller method should do ONE thing: extract data → call ONE service method → return response.\n\n## Example Structure\n\n### ✅ GOOD - Thin Controller\n\n```typescript\nimport type { Request, Response, NextFunction } from \"express\";\nimport { AuthService } from \".\u002Fauth.service\";\n\nexport class AuthController {\n  private authService: AuthService;\n\n  constructor() {\n    this.authService = new AuthService();\n    this.register = this.register.bind(this);\n  }\n\n  async register(req: Request, res: Response, next: NextFunction) {\n    try {\n      \u002F\u002F 1. Extract data from body (already validated by schema)\n      const { names, lastNames, email, password } = req.body;\n\n      \u002F\u002F 2. Check business rule (NOT data validation)\n      const existingPersona = await this.authService.findUserByEmail(email);\n      if (existingPersona) {\n        return res.status(409).json({\n          error: \"El usuario ya existe\",\n        });\n      }\n\n      \u002F\u002F 3. Call service\n      const persona = await this.authService.createUser(\n        names,\n        lastNames,\n        email,\n        password,\n      );\n\n      \u002F\u002F 4. Return response\n      return res.status(201).json({\n        message: \"Usuario creado correctamente\",\n        user: persona,\n      });\n    } catch (error: any) {\n      console.error(\"Error en register:\", error);\n      return res.status(500).json({\n        error: error.message || \"Ocurrió un error interno al registrar el usuario.\",\n      });\n    }\n  }\n\n  async getUser(req: Request, res: Response, next: NextFunction) {\n    try {\n      \u002F\u002F req.user is guaranteed to exist - middleware handled auth\n      const { password: _, ...safeUser } = req.user;\n      return res.json(safeUser);\n    } catch (error) {\n      next(error);\n    }\n  }\n}\n```\n\n### ❌ BAD - Fat Controller (What NOT to Do)\n\n```typescript\n\u002F\u002F ❌ BAD: Re-validating data that schema already validated\nasync register(req: Request, res: Response, next: NextFunction) {\n  const { names, email, password } = req.body;\n\n  \u002F\u002F This is WRONG - schema already validated this!\n  if (!names || names.length \u003C 1) {\n    return res.status(400).json({ error: \"Name is required\" });\n  }\n  if (!email || !email.includes(\"@\")) {\n    return res.status(400).json({ error: \"Invalid email\" });\n  }\n  if (password.length \u003C 8) {\n    return res.status(400).json({ error: \"Password too short\" });\n  }\n  \u002F\u002F ...\n}\n\n\u002F\u002F ❌ BAD: Checking auth that middleware already handled\nasync getUser(req: Request, res: Response, next: NextFunction) {\n  \u002F\u002F This is WRONG - middleware already checked this!\n  if (!req.user) {\n    return res.status(401).json({ error: \"No autorizado\" });\n  }\n  \u002F\u002F ...\n}\n\n\u002F\u002F ❌ BAD: Calling prisma directly instead of using service\nasync updateProfile(req: Request, res: Response) {\n  const { id_persona } = req.user;\n  const { name, email } = req.body;\n\n  \u002F\u002F This is WRONG - should use service\n  await prisma.persona.update({\n    where: { id_persona },\n    data: { nombres: name, correo_electronico: email },\n  });\n}\n```\n\n## Request Flow (Correct Order)\n\n```\nRequest → [validateSchema] → [authMiddleware] → [roleMiddleware] → Controller → Service → Prisma\n              ↑                         ↑                    ↑\n         backend-schema          backend-router       backend-router\n         (validates data)        (validates auth)      (validates roles)\n```\n\n## Controller Responsibilities by Layer\n\n| What | Where |\n|------|-------|\n| Data type\u002Flength validation | `backend-schema` (express-validator) |\n| Authentication check | `AuthMiddleware` (backend-router) |\n| Authorization\u002FRole check | RoleMiddleware (backend-router) |\n| Business logic | `backend-service` |\n| HTTP Response | Controller (this skill) |\n\n## Custom Request Interface Example\n\n```typescript\n\u002F\u002F src\u002Fauth\u002Finterfaces\u002FauthRequest.interface.ts\nimport type { Request } from \"express\";\nimport type { persona } from \"@prisma\u002Fclient\";\n\nexport interface AuthRequest extends Request {\n  user?: Partial\u003Cpersona>;\n}\n\n\u002F\u002F Usage in controller\nasync register(req: AuthRequest, res: Response) {\n  \u002F\u002F req.user is properly typed now\n}\n```\n\n## Error Handling in Controllers\n\n```typescript\nasync someEndpoint(req: Request, res: Response, next: NextFunction) {\n  try {\n    \u002F\u002F Business logic\n    const result = await this.service.doSomething();\n    \n    return res.status(200).json(result);\n  } catch (error: any) {\n    \u002F\u002F For known business errors (from service)\n    if (error.message.includes(\"Business error\")) {\n      return res.status(400).json({ error: error.message });\n    }\n    \n    \u002F\u002F For unexpected errors - let global handler deal with it\n    next(error);\n  }\n}\n```\n\n## Summary: What Belongs in Controller\n\n✅ **DO:**\n- Extract `req.body` data\n- Use `req.user` (trust the middleware)\n- Call service methods\n- Return HTTP responses with proper status codes\n- Handle errors and return user-friendly messages\n\n❌ **DON'T:**\n- Validate data types, lengths, formats (use schema)\n- Check if user is authenticated (use middleware)\n- Check user roles (use middleware)\n- Call prisma directly for business logic (use service)\n- Log with console.log (services handle logging)\n",{},"main","3d76dc018cdded158c4f49df5edb56829229d265","skills\u002Fbackend-controller",0,"https:\u002F\u002Fgithub.com\u002Fdavidleonmayor\u002Fproyecto-grado-unimayor","https:\u002F\u002Fproyecto-grado-unimayor-8rtknmpjt-davidleonmayors-projects.vercel.app\u002Fadmin","https:\u002F\u002Fgithub.com\u002Fdavidleonmayor","Full-stack developer focused on microservices backend","2026-11-11 00:05:51",{"source":7,"skillName":5,"commands":40,"prompt":50,"zipAvailable":51},[41,44,47],{"tool":42,"command":43},"npx","npx skills add davidleonmayor\u002Fproyecto-grado-unimayor --skill backend-controller",{"tool":45,"command":46},"bunx","bunx skills add davidleonmayor\u002Fproyecto-grado-unimayor --skill backend-controller",{"tool":48,"command":49},"pnpm","pnpm dlx skills add davidleonmayor\u002Fproyecto-grado-unimayor --skill backend-controller","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fdavidleonmayor\u002Fproyecto-grado-unimayor\u002Fblob\u002Fmain\u002Fskills\u002Fbackend-controller\u002FSKILL.md",true,[],[54,71,92,112,124,145],{"skillNo":55,"name":56,"nameZh":6,"description":57,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":58,"licenseFileUrl":6,"sourceHtmlUrl":59,"categories":60,"keywords":64,"publishedAt":26},"01M6C2DVB157H69PFWB1XXBVZS","skill-sync","Synchronizes skill metadata to AGENTS.md Auto-invoke sections. Trigger: When syncing skill metadata to AGENTS.md.","Apache-2.0","https:\u002F\u002Fgithub.com\u002Fdavidleonmayor\u002Fproyecto-grado-unimayor\u002Fblob\u002Fmain\u002Fskills\u002Fskill-sync\u002FSKILL.md",[61],{"categoryNo":62,"categoryName":63,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000023","Skill与智能体开发",[65,66,67,68,69,70],"sync","skill sync","metadata","cloud","prowler","auto",{"skillNo":72,"name":73,"nameZh":6,"description":74,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":58,"licenseFileUrl":6,"sourceHtmlUrl":75,"categories":76,"keywords":78,"publishedAt":26},"01M6C2DRSV8QKEFA2J94YQKWK1","skill-creator","Creates new AI agent skills following the Agent Skills spec. Trigger: When user asks to create a new skill, add agent instructions, or document patterns for AI.","https:\u002F\u002Fgithub.com\u002Fdavidleonmayor\u002Fproyecto-grado-unimayor\u002Fblob\u002Fmain\u002Fskills\u002Fskill-creator\u002FSKILL.md",[77],{"categoryNo":62,"categoryName":63,"taxonomyType":20,"taxonomyTypeDesc":21},[79,80,81,82,68,69,83,84,85,86,87,88,89,90,91],"skill creator","spec","ai agent","create","Bash","grep","read","write","edit","instructions","user","webfetch","add agent",{"skillNo":93,"name":94,"nameZh":6,"description":95,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":58,"licenseFileUrl":6,"sourceHtmlUrl":96,"categories":97,"keywords":102,"publishedAt":26},"01M6C2DP81DY28MKM0RKFWJQHX","skill-commit","Creates professional git commits following conventional-commits format. Trigger: When creating commits, after completing code changes, when user asks to commit.","https:\u002F\u002Fgithub.com\u002Fdavidleonmayor\u002Fproyecto-grado-unimayor\u002Fblob\u002Fmain\u002Fskills\u002Fskill-commit\u002FSKILL.md",[98,101],{"categoryNo":99,"categoryName":100,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000009","前端开发",{"categoryNo":62,"categoryName":63,"taxonomyType":20,"taxonomyTypeDesc":21},[103,104,105,24,106,89,107,108,109,110,111],"commit","skill commit","landing","code","frontend","git commit","git","commits","format",{"skillNo":113,"name":114,"nameZh":6,"description":115,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":58,"licenseFileUrl":6,"sourceHtmlUrl":116,"categories":117,"keywords":118,"publishedAt":26},"01M6C2DMHK8HST60TFQHMK8D94","project-info","General monorepo structure and component overview. Trigger: When AI needs project context or component locations.","https:\u002F\u002Fgithub.com\u002Fdavidleonmayor\u002Fproyecto-grado-unimayor\u002Fblob\u002Fmain\u002Fskills\u002Fproject-info\u002FSKILL.md",[],[119,120,121,122,123],"project info","component","monorepo","context","overview",{"skillNo":125,"name":126,"nameZh":6,"description":127,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":58,"licenseFileUrl":6,"sourceHtmlUrl":128,"categories":129,"keywords":136,"publishedAt":26},"01M6C2DHT1Q0Q87WRYBCBH3QHG","frontend-context","Frontend architecture, patterns and conventions for the Next.js 15 app. Trigger: When working on frontend code, components, pages, or API integration.","https:\u002F\u002Fgithub.com\u002Fdavidleonmayor\u002Fproyecto-grado-unimayor\u002Fblob\u002Fmain\u002Fskills\u002Ffrontend-context\u002FSKILL.md",[130,131],{"categoryNo":99,"categoryName":100,"taxonomyType":20,"taxonomyTypeDesc":21},{"categoryNo":132,"categoryName":133,"taxonomyType":134,"taxonomyTypeDesc":135},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[107,122,137,138,139,140,106,141,142,143,144],"frontend context","api","components","architecture","frontend architecture","integration","Next.js","js",{"skillNo":146,"name":147,"nameZh":6,"description":148,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":58,"licenseFileUrl":6,"sourceHtmlUrl":149,"categories":150,"keywords":155,"publishedAt":26},"01M6C2DESFJR14Z3G38F4KZCSG","express-mocks-testing","Unit testing pattern for Express 5 controllers using node-mocks-http and @prisma\u002Fclient mocks. Trigger: When writing unit tests for backend controllers, testing Express routes, or avoiding Supertest\u002FServer instantiation.","https:\u002F\u002Fgithub.com\u002Fdavidleonmayor\u002Fproyecto-grado-unimayor\u002Fblob\u002Fmain\u002Fskills\u002Fexpress-mocks-testing\u002FSKILL.md",[151,154],{"categoryNo":152,"categoryName":153,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000008","测试与质量",{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},[156,157,158,159,160,161,162,163,24],"express mocks testing","testing","writing","unit tests","http","server","programming","node"]