[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6C2DCXFSYWZCQWMPGWWQJQ7":3,"skill-same:01M6C2DCXFSYWZCQWMPGWWQJQ7":52},{"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":25,"linkOnly":26,"markdownContent":27,"markdownBody":27,"frontMatter":28,"refName":29,"commitSha":30,"skillDirectory":31,"forksCount":32,"repositoryHtmlUrl":33,"repositoryHomepageUrl":34,"ownerHtmlUrl":35,"ownerBlogUrl":6,"ownerDescription":36,"updatedAt":37,"install":38,"resources":51},"01M6C2DCXFSYWZCQWMPGWWQJQ7","backend-service",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-service\u002FSKILL.md",[17],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000010","后端与接口","SCENARIO","使用场景",[23,24],"backend","backend service","2026-11-10 10:12:30",false,"# Backend Service\n\n## Overview\nThis skill defines the convention for backend service classes that interact with external resources (Database, Email, Redis, Webhooks, External APIs, etc.). Services encapsulate business logic and data access, keeping controllers thin and focused on HTTP concerns.\n\n## Trigger\nWhen creating or updating service classes for backend features (e.g., `auth.service.ts`, `webhook.service.ts`, `email.service.ts`).\n\n## Do's\n\n- **Use Classes**: Define services as classes (e.g., `AuthService`, `WebhookService`). Export a single class per file.\n- **Singleton or Instance**: For database services, import `prisma` singleton from `..\u002Fconfig\u002Fprisma`. For external services, initialize in constructor or import configured clients.\n- **Try-Catch Every Public Method**: Wrap ALL external calls (DB, API, Email) in `try-catch` blocks. Log the raw error with context and throw a user-friendly error message.\n- **Use Logger**: Import `logger` from `..\u002Fconfig` and log errors properly: `logger.error(\"Error doing X:\", error)`. Never use `console.log` or `console.error` in production code.\n- **Specific Error Messages**: Throw errors with clear, user-facing messages in Spanish. For known error codes (like Prisma's `P2002`), check `error.code` and provide specific messages.\n- **Async\u002FAwait**: Use `async\u002Fawait` for all methods that perform I\u002FO operations. Mark method signatures with `async` and return `Promise\u003CT>`.\n- **Single Responsibility**: A service method should do ONE thing (e.g., `findUserByEmail`, `createUser`). Don't mix concerns.\n- **Type Safety**: Define proper return types. If using Prisma, use `Prisma.modelGetPayload\u003C{}>`, `Prisma.modelWhereUniqueInput`, or the resulting model type. Avoid `any`.\n- **Dependency Injection**: Receive dependencies (like `prisma`, email clients, redis) via constructor parameters or import them as singletons from `config\u002F`.\n\n## Don'ts\n\n- **Don't handle HTTP\u002FResponse logic**: Services must NEVER call `res.status()`, `res.json()`, or send HTTP responses. That's the Controller's job.\n- **Don't import Express types**: Services should not import `Request` or `Response` from `express`.\n- **Don't have empty catch blocks**: Always log the error and re-throw (or throw a new meaningful error).\n- **Don't return `any` type**: Define proper return types. Use TypeScript strict mode.\n- **Don't put validation logic**: Data validation (types, lengths, formats) belongs in `*.schema.ts` (using `express-validator`), not in the service.\n- **Don't use `console.log\u002Ferror`**: Always use the `logger` from `config\u002F`.\n- **Don't mix service types**: A service should focus on ONE type of external resource (DB, Email, Redis). Don't mix DB calls with email sending in the same method unless it's orchestration.\n\n## Example Structure\n\n### Database Service (e.g., AuthService)\n\n```typescript\nimport { prisma } from \"..\u002Fconfig\u002Fprisma\";\nimport { logger } from \"..\u002Fconfig\";\nimport type { Prisma } from \"@prisma\u002Fclient\";\n\nexport class AuthService {\n  \u002F**\n   * Creates a new user in the database.\n   * @param email - User's email address\n   * @param password - Plain text password (will be hashed)\n   * @returns The created user object\n   *\u002F\n  async createUser(email: string, password: string): Promise\u003CPrisma.personaGetPayload\u003C{}>> {\n    try {\n      const hashedPassword = await hashPassword(password);\n      \n      const user = await prisma.persona.create({\n        data: {\n          correo_electronico: email.toLowerCase().trim(),\n          password: hashedPassword,\n          \u002F\u002F ... other fields\n        },\n      });\n      \n      return user;\n    } catch (error: any) {\n      logger.error(\"Error al crear usuario:\", error);\n      \n      \u002F\u002F Handle specific Prisma errors\n      if (error.code === \"P2002\") {\n        throw new Error(\"El correo electrónico ya está registrado.\");\n      }\n      \n      throw new Error(\"Error al crear el usuario en la base de datos.\");\n    }\n  }\n\n  \u002F**\n   * Finds a user by their email address.\n   * @param email - Email to search for\n   * @returns User object or null if not found\n   *\u002F\n  async findUserByEmail(email: string): Promise\u003CPrisma.personaGetPayload\u003C{}> | null> {\n    try {\n      return await prisma.persona.findUnique({\n        where: { correo_electronico: email },\n      });\n    } catch (error) {\n      logger.error(\"Error al buscar usuario por email:\", error);\n      throw new Error(\"Error al consultar el usuario en la base de datos.\");\n    }\n  }\n}\n```\n\n### External Service (e.g., WebhookService, EmailService)\n\n```typescript\nimport { logger } from \"..\u002Fconfig\";\nimport { prisma } from \"..\u002Fconfig\u002Fprisma\";\nimport type { WebhookSubscription } from \"@prisma\u002Fclient\";\n\nexport class WebhookService {\n  private readonly timeout: number = 5000;\n\n  \u002F**\n   * Dispatches an event to all active webhook subscriptions.\n   * @param topic - The event topic (e.g., \"MESSAGE_CREATED\")\n   * @param payload - The event payload to send\n   *\u002F\n  async dispatch(topic: string, payload: Record\u003Cstring, unknown>): Promise\u003Cvoid> {\n    try {\n      const subscriptions = await prisma.webhook_subscription.findMany({\n        where: { topic, is_active: true },\n      });\n\n      if (subscriptions.length === 0) {\n        logger.info(`[WebhookService] No active subscriptions for topic: ${topic}`);\n        return;\n      }\n\n      logger.info(`[WebhookService] Dispatching ${topic} to ${subscriptions.length} subscribers`);\n\n      \u002F\u002F Fire and forget with error handling\n      for (const sub of subscriptions) {\n        this.sendPayload(sub.url, payload, sub.secret).catch((err) => {\n          logger.error(`[WebhookService] Failed to dispatch to ${sub.url}:`, err);\n        });\n      }\n    } catch (error) {\n      logger.error(\"[WebhookService] Error fetching subscriptions:\", error);\n    }\n  }\n\n  private async sendPayload(\n    url: string,\n    payload: Record\u003Cstring, unknown>,\n    secret: string | null,\n  ): Promise\u003Cvoid> {\n    const headers: Record\u003Cstring, string> = {\n      \"Content-Type\": \"application\u002Fjson\",\n      \"User-Agent\": \"GestionProyectos-Webhook-Dispatcher\u002F1.0\",\n    };\n\n    if (secret) {\n      headers[\"X-Webhook-Secret\"] = secret;\n    }\n\n    try {\n      const response = await fetch(url, {\n        method: \"POST\",\n        headers,\n        body: JSON.stringify(payload),\n        \u002F\u002F Consider adding timeout via AbortController in production\n      });\n\n      if (!response.ok) {\n        throw new Error(`HTTP Error: ${response.status} ${response.statusText}`);\n      }\n\n      logger.info(`[WebhookService] Successfully delivered to ${url}`);\n    } catch (error) {\n      logger.error(`[WebhookService] Failed to send to ${url}:`, error);\n      throw error; \u002F\u002F Re-throw for the caller's catch\n    }\n  }\n}\n```\n\n## Method Naming Conventions\n\n| Operation | Naming Pattern | Example |\n|-----------|----------------|---------|\n| Create | `create\u003CResource>` | `createUser`, `createMessage`, `sendEmail` |\n| Read (one) | `find\u003CResource>By\u003CField>` | `findUserByEmail`, `findUserById` |\n| Read (many) | `list\u003CResources>` or `get\u003CResources>` | `listActiveUsers`, `getInboxMessages` |\n| Update | `update\u003CResource>` | `updatePerson`, `updatePassword` |\n| Delete | `delete\u003CResource>` or `softDelete\u003CResource>` | `softDeleteMessage` |\n| Check\u002FVerify | `is\u003CCondition>` or `check\u003CThing>` | `isEmailTaken`, `checkPassword` |\n| Dispatch\u002FSend | `dispatch\u003CEvent>` or `send\u003CThing>` | `dispatchWebhook`, `sendEmail` |\n\n## Error Handling Pattern\n\n```typescript\ntry {\n  \u002F\u002F External call (DB, API, etc.)\n  return await externalCall();\n} catch (error: any) {\n  logger.error(\"Context-specific error message:\", error);\n  \n  \u002F\u002F Optional: Handle specific error codes\n  if (error.code === \"P2002\") {\n    throw new Error(\"User-friendly message for this specific case.\");\n  }\n  \n  \u002F\u002F Generic fallback\n  throw new Error(\"Error al realizar la operación en la base de datos.\");\n}\n```\n\n## Database Services vs External Services\n\n### Database Services (e.g., AuthService, MessagingService)\n- Import `prisma` from `config\u002Fprisma` (singleton)\n- Use Prisma Client methods (`create`, `findUnique`, `update`, `delete`, `findMany`)\n- Handle Prisma error codes (`P2002`, `P2025`, `P2003`)\n- Return Prisma model types or typed results\n- Always use `async\u002Fawait` for DB calls\n\n### External Services (e.g., WebhookService, EmailService)\n- Import external clients from `config\u002F` or initialize them in constructor\n- Handle API errors, timeouts, network errors\n- Log with context about the external service\n- Return simple success\u002Ffailure or parsed response data\n- Consider retry logic or queue systems for resiliency\n- Use `AbortController` for fetch timeouts in production\n\n## Logging Best Practices\n\n```typescript\n\u002F\u002F ✅ GOOD - With context\nlogger.error(`Error updating user ${userId}:`, error);\nlogger.info(`[WebhookService] Dispatched event to ${url}`);\n\n\u002F\u002F ❌ BAD - No context\nconsole.log(error);\nconsole.error(\"Error occurred\");\n```\n\n",{},"main","3d76dc018cdded158c4f49df5edb56829229d265","skills\u002Fbackend-service",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":39,"prompt":49,"zipAvailable":50},[40,43,46],{"tool":41,"command":42},"npx","npx skills add davidleonmayor\u002Fproyecto-grado-unimayor --skill backend-service",{"tool":44,"command":45},"bunx","bunx skills add davidleonmayor\u002Fproyecto-grado-unimayor --skill backend-service",{"tool":47,"command":48},"pnpm","pnpm dlx skills add davidleonmayor\u002Fproyecto-grado-unimayor --skill backend-service","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fdavidleonmayor\u002Fproyecto-grado-unimayor\u002Fblob\u002Fmain\u002Fskills\u002Fbackend-service\u002FSKILL.md",true,[],[53,70,91,111,123,144],{"skillNo":54,"name":55,"nameZh":6,"description":56,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":57,"licenseFileUrl":6,"sourceHtmlUrl":58,"categories":59,"keywords":63,"publishedAt":25},"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",[60],{"categoryNo":61,"categoryName":62,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000023","Skill与智能体开发",[64,65,66,67,68,69],"sync","skill sync","metadata","cloud","prowler","auto",{"skillNo":71,"name":72,"nameZh":6,"description":73,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":57,"licenseFileUrl":6,"sourceHtmlUrl":74,"categories":75,"keywords":77,"publishedAt":25},"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",[76],{"categoryNo":61,"categoryName":62,"taxonomyType":20,"taxonomyTypeDesc":21},[78,79,80,81,67,68,82,83,84,85,86,87,88,89,90],"skill creator","spec","ai agent","create","Bash","grep","read","write","edit","instructions","user","webfetch","add agent",{"skillNo":92,"name":93,"nameZh":6,"description":94,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":57,"licenseFileUrl":6,"sourceHtmlUrl":95,"categories":96,"keywords":101,"publishedAt":25},"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",[97,100],{"categoryNo":98,"categoryName":99,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000009","前端开发",{"categoryNo":61,"categoryName":62,"taxonomyType":20,"taxonomyTypeDesc":21},[102,103,104,23,105,88,106,107,108,109,110],"commit","skill commit","landing","code","frontend","git commit","git","commits","format",{"skillNo":112,"name":113,"nameZh":6,"description":114,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":57,"licenseFileUrl":6,"sourceHtmlUrl":115,"categories":116,"keywords":117,"publishedAt":25},"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",[],[118,119,120,121,122],"project info","component","monorepo","context","overview",{"skillNo":124,"name":125,"nameZh":6,"description":126,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":57,"licenseFileUrl":6,"sourceHtmlUrl":127,"categories":128,"keywords":135,"publishedAt":25},"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",[129,130],{"categoryNo":98,"categoryName":99,"taxonomyType":20,"taxonomyTypeDesc":21},{"categoryNo":131,"categoryName":132,"taxonomyType":133,"taxonomyTypeDesc":134},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[106,121,136,137,138,139,105,140,141,142,143],"frontend context","api","components","architecture","frontend architecture","integration","Next.js","js",{"skillNo":145,"name":146,"nameZh":6,"description":147,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":57,"licenseFileUrl":6,"sourceHtmlUrl":148,"categories":149,"keywords":154,"publishedAt":25},"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",[150,153],{"categoryNo":151,"categoryName":152,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000008","测试与质量",{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},[155,156,157,158,159,160,161,162,23],"express mocks testing","testing","writing","unit tests","http","server","programming","node"]