Agent Skills › YosemiteCrew/Yosemite-Crew › backend-patterns

backend-patterns

GitHub

针对后端应用开发,规范路由、控制器、服务及模型的分层架构。涵盖Zod验证、Prisma数据访问、BullMQ队列及第三方集成,指导新接口与业务逻辑实现。

.claude/skills/backend-patterns/SKILL.md YosemiteCrew/Yosemite-Crew

Trigger Scenarios

创建新的后端端点或控制器 实现后端服务或数据模型 配置消息队列或外部集成

Install

npx skills add YosemiteCrew/Yosemite-Crew --skill backend-patterns -g -y
More Options

Non-standard path

npx skills add https://github.com/YosemiteCrew/Yosemite-Crew/tree/main/.claude/skills/backend-patterns -g -y

Use without installing

npx skills use YosemiteCrew/Yosemite-Crew@backend-patterns

指定 Agent (Claude Code)

npx skills add YosemiteCrew/Yosemite-Crew --skill backend-patterns -a claude-code -g -y

安装 repo 全部 skill

npx skills add YosemiteCrew/Yosemite-Crew --all -g -y

预览 repo 内 skill

npx skills add YosemiteCrew/Yosemite-Crew --list

SKILL.md

Frontmatter
{
    "name": "backend-patterns",
    "description": "Use when working in apps\/backend — new endpoints, controllers, services, models, queues\/workers, or integrations. Covers the Router to Controller to Service to Model architecture, Zod validation, Prisma\/PostgreSQL data access, Winston logging, BullMQ jobs, and FHIR\/IDEXX\/Merck integrations."
}

Backend Patterns — Yosemite Crew

Description

Use this skill when working on apps/backend. Covers Express.js architecture, service/controller patterns, validation, error handling, and healthcare-specific integrations.

TRIGGER: any task in apps/backend — new endpoints, services, models, or integrations.


Architecture

apps/backend/src/
  routers/        ← Express route definitions (thin — just register handlers)
  controllers/    ← Request/response handling, input validation
  services/       ← Business logic (no req/res objects here)
  models/         ← legacy data models; no new files here (use Prisma via packages/database)
  queues/         ← BullMQ job definitions
  workers/        ← BullMQ worker processors
  integrations/   ← External services (IDEXX, Merck, Stripe, Firebase, AWS)

Pattern: Router → Controller → Service → Model

Controllers call services. Services call models. Never put business logic in controllers or routers.


Validation

Use Zod for request validation. Never trust raw req.body.

import { z } from 'zod';

const CreateAppointmentSchema = z.object({
  patientId: z.string().uuid(),
  date: z.string().datetime(),
});

// In controller:
const data = CreateAppointmentSchema.parse(req.body);

Database

  • PostgreSQL via Prisma is the database for all new code; Prisma owns schema + migrations (see @yosemite-crew/database).
  • Prisma only for new persistence. All new models and queries go through Prisma (packages/database); do not add new files under src/models/ — this matches apps/backend/AGENTS.md.
  • Never access data directly from controllers — always go through services/models.

Authentication

SuperTokens is the auth provider behind the provider-neutral boundary in packages/auth (#1672), initialized by initSuperTokens in app.ts. Product code uses the session guards in src/middlewares/auth.ts and never imports a provider SDK (eslint-enforced). Pick the guard by product surface:

  • requireWebAuth - staff / PIMS web routes.
  • requireMobileAuth - pet-parent mobile routes.
  • requireAnyAuth - routes genuinely shared by both.

Never roll custom auth.

Authorization: derive the tenant from the resource

Authentication only proves who is calling. withOrgPermissions() then proves the caller belongs to the organisation named by the request (route param, x-org-id, query, or body). On a route addressed by a resource id that is not enough on its own - a caller can name an organisation they legitimately belong to while addressing another tenant's record.

  • On an id-addressed route, use the resource-derived middleware so the organisation comes from the record: withAppointmentOrgPermissions, withInvoiceOrgPermissions, withPaymentOrgPermissions, withPaymentIntentOrgPermissions, withTaskOrgPermissions, withInventoryItemOrgPermissions, withEncounterOrgPermissions, withCaseOrgPermissions, withRenderedDocumentOrgPermissions, withRoomUnitOrgPermissions, withRoomUnitGroupOrgPermissions. Add a new one via withResourceOrgPermissions rather than hand-rolling a lookup.
  • In a controller, scope on (req as OrgRequest).organisationId - the value the middleware authorized. If the request also carries an organisation, it must agree with that value; reject a mismatch rather than preferring it.
  • Take the organisation as a required service argument, never organisationId?. Prisma drops undefined where-fields, so an optional scope silently becomes an unfiltered, cross-tenant query. Required turns that into a build error.
  • Document has no organisation column - scope document queries through documentWhereForOrg() (src/services/document-scope.ts), which expresses the patientOrganisation join and the PMS visibility flag once.
  • requirePermission([a, b]) is any-of. That is the intended idiom for an :any/:own pair of the same resource. An array naming two different resources grants each to holders of the other - require both instead.
  • Read the acting user from the verified session (req.userId). Do not use resolveUserIdFromRequest for an authorization decision: it falls back to a client-supplied x-user-id header. It is fine for attribution only.

Background Jobs

BullMQ is the queue system. Jobs go in queues/, processors in workers/.

// Never process jobs inline in a request handler
// Always enqueue and let a worker handle async operations
await emailQueue.add('send-reminder', { appointmentId });

Healthcare Integrations

  • FHIR types from @yosemite-crew/fhir — use these, never invent custom health data shapes.
  • IDEXX and Merck integrations live in src/integrations/ — extend there, never inline.

Logging

Use Winston for all logging. Never use console.log in production code.

import logger from 'src/utils/logger';
logger.info('Appointment created', { appointmentId });
logger.error('Payment failed', { error, userId });

Gotchas

  • Do not refactor backend architecture unless explicitly asked — the user's CLAUDE.md is explicit about this.
  • Zod .parse() throws on invalid input — use .safeParse() when you want to handle errors gracefully.
  • BullMQ jobs are persisted in Redis — make job processors idempotent.
  • All Stripe webhook handlers must verify the signature before processing.
  • Firebase Admin SDK is initialized once — never re-initialize it in a handler.

Version History

  • 3726483 Current 2026-08-20 13:37

Same Skill Collection

.agents/skills/agent-loop/SKILL.md
.agents/skills/backend-patterns/SKILL.md
.agents/skills/code-review/SKILL.md
.agents/skills/desktop-sonar/SKILL.md
.agents/skills/frontend-design/SKILL.md
.agents/skills/frontend-sonar/SKILL.md
.agents/skills/frontend-testing/SKILL.md
.agents/skills/mobile-patterns/SKILL.md
.agents/skills/monorepo-ops/SKILL.md
.agents/skills/react-doctor/SKILL.md
.agents/skills/yosemite-client-communications/SKILL.md
.agents/skills/yosemite-data-migration-audit/SKILL.md
.agents/skills/yosemite-inventory-planning/SKILL.md
.agents/skills/yosemite-practice-workflow-audit/SKILL.md
.agents/skills/yosemite-staff-onboarding/SKILL.md
.agents/skills/yosemite-vet-software-buyer/SKILL.md
.agents/skills/yosemite-vet-visit-prep/SKILL.md
.claude/skills/agent-loop/SKILL.md
.claude/skills/code-review/SKILL.md
.claude/skills/desktop-sonar/SKILL.md
.claude/skills/frontend-design/SKILL.md
.claude/skills/frontend-sonar/SKILL.md
.claude/skills/frontend-testing/SKILL.md
.claude/skills/mobile-patterns/SKILL.md
.claude/skills/monorepo-ops/SKILL.md
.claude/skills/react-doctor/SKILL.md
.claude/skills/yosemite-client-communications/SKILL.md
.claude/skills/yosemite-data-migration-audit/SKILL.md
.claude/skills/yosemite-inventory-planning/SKILL.md
.claude/skills/yosemite-practice-workflow-audit/SKILL.md
.claude/skills/yosemite-staff-onboarding/SKILL.md
.claude/skills/yosemite-vet-software-buyer/SKILL.md
.claude/skills/yosemite-vet-visit-prep/SKILL.md

Metadata

Files
0
Version
6932903
Hash
37abce65
Indexed
2026-08-20 13:37

ホーム - Wiki
Copyright © 2011-2026 iteam. Current version is 2.155.2. UTC+08:00, 2026-09-29 08:08
浙ICP备14020137号-1