본문으로 건너뛰기
cccskills
무료GitHub에 공개

node-typescript-service

Use when building or extending a TypeScript service on Node.js - Fastify, NestJS, Express or Hono, runtime validation at the boundary, strict types, the repository's ORM and migrations, async error handling, graceful shutdown

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

설치 방법 보기

포함된 파일(1)

  • SKILL.md6.0 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

TypeScript Service on Node.js

Overview

A Node.js service is TypeScript at compile time and untyped JavaScript at run time. Every type at the edge — a request body, a header, an environment variable, a queue message, a row from a raw query — is a claim until something checks it.

Core principle: the repository's framework, package manager, module system and ORM win. Parse input at the boundary into typed values; keep the domain free of the framework.

Choosing, only when there is nothing to follow

  • Fastify for a new HTTP service: schema-first routes, app.inject() for tests, pino logging built in.
  • NestJS when the service is domain-rich and the team wants modules, DI and decorators (the Quarkus/Spring of Node).
  • Express 5 or Hono only to match a repository or a platform that already uses them.

Layers

LayerResponsibility
Route / controllerParse and validate input, call one service, map the result and errors to HTTP
ServiceUse-case orchestration and business rules; takes plain typed values
DomainTypes, invariants, pure functions — no framework import
AdapterDatabase, outbound HTTP, queues — behind an interface the service depends on

Patterns

  • Package manager from the lockfile: pnpm-lock.yaml → pnpm, yarn.lock → yarn, package-lock.json → npm; install with the frozen form (pnpm install --frozen-lockfile, npm ci). Node version from .nvmrc, .node-version or engines; a new service targets the current Active LTS.
  • Module system from package.json: "type": "module" means ESM (relative imports carry .js under NodeNext); otherwise CommonJS. Never mix them in one package.
  • strict: true. No any; unknown at the boundary, narrowed by a parser. No non-null ! to silence a real undefined.
  • Validate at runtime at the boundary: zod (or TypeBox / Fastify JSON schema, class-validator in NestJS — whatever the repository uses) on body, params, query and headers; derive the TypeScript type from the schema (z.infer) so the two cannot drift. Environment variables are parsed once at startup by the same means and the process exits on a bad one.
  • Errors in one shape: a central error handler (setErrorHandler, a Nest exception filter, Express 5's error middleware) maps domain errors to 400/404/409 problem+json; an unknown error is logged with its stack and answered 500 without it.
  • No floating promises: every promise is awaited, returned or explicitly handled (@typescript-eslint/no-floating-promises catches the rest). An unhandled rejection crashes the process in current Node.
  • Outbound calls have a deadline: fetch(url, { signal: AbortSignal.timeout(5_000) }) or the client's own timeout; retries only for idempotent calls (resilient-io-and-jobs).
  • Never block the event loop: no *Sync file or crypto calls on a request path, no unbounded JSON.parse of a large body, CPU-heavy work in a worker thread or a job.
  • Database: the repository's tool — Prisma, Drizzle, Kysely, TypeORM, MikroORM or pg — with parameters, never string-built SQL ($queryRawUnsafe and sql.raw with input are bugs). Migrations through that tool (prisma migrate dev --name, drizzle-kit generate), committed with the schema change; postgres-migrations covers safe DDL.
  • Logging: pino (Fastify's logger, nestjs-pino) with structured fields, request id bound per request; no console.log in service code; redact secrets and tokens (redact option).
  • Graceful shutdown: on SIGTERM stop accepting connections, finish in-flight requests, close the pool, then exit — Fastify close(), Nest enableShutdownHooks().
  • Dependencies: a new package is a decision (reuse-before-you-write) — exact name from the registry, added with the repository's package manager, lockfile in the same commit.

Worked Example

POST /tasks in Fastify with zod:

const CreateTask = z.object({ title: z.string().trim().min(1).max(200) });

export function taskRoutes(app: FastifyInstance, service: TaskService) {
  app.post("/tasks", async (req, reply) => {
    const parsed = CreateTask.safeParse(req.body);
    if (!parsed.success) {
      return reply.code(400).type("application/problem+json").send(problemFrom(parsed.error));
    }
    const task = await service.create(parsed.data.title, req.user.id);
    return reply.code(201).header("location", `/tasks/${task.id}`).send(toResponse(task));
  });
}

TaskService.create takes a string and an owner id and knows nothing of Fastify; Task.create enforces the 200-character rule again for callers that are not HTTP.

Verify

The repository's scripts first (list_component_checks, package.json, CI): typically npm run typecheck (or tsc --noEmit), npm run lint, npm test, npm run build. For the runtime check build once and run the output — npm run build, then PORT=18080 node dist/main.js in the background with its pid recorded — not a watcher (npm run dev, tsx watch, nodemon), which never exits and restarts under you.

Common Mistakes

  • Trusting req.body as CreateTaskDto — a cast is not a check.
  • async route handlers in Express 4 without a wrapper: the rejection never reaches the error middleware.
  • A request path that awaits in a for loop where one query or Promise.all with a bound would do — or Promise.all over an unbounded list.
  • process.env.X! scattered through the code instead of one parsed config object.

Red Flags

  • any in a new signature, or // @ts-ignore / @ts-expect-error added to get a build green.
  • String-concatenated SQL, or a raw query helper fed user input.
  • A promise created and never awaited in a request handler.
  • console.log of a request body that may hold a token or a password.

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

Use when writing acceptance criteria for a task - express each as an observable Given/When/Then that QA can execute, including negative cases

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

makifbaysal/tasktrooper1172026년 10월 18일 업데이트

Use when the diff adds or changes an endpoint, resolver, RPC, job or query that takes an object id, a role check, a request binding or a tenant filter - BOLA/IDOR, function-level authorization, mass assignment and tenant scoping

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

makifbaysal/tasktrooper1172026년 10월 18일 업데이트

Use on every UI change - semantic HTML, labels for controls, keyboard-navigable dialogs/menus, visible focus, and never color as the only signal

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

makifbaysal/tasktrooper1172026년 10월 18일 업데이트

Use when a task changes any screen, form, dialog, menu or control - Lighthouse/axe scan of the changed screens, a keyboard walk, and the thresholds that fail a task

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

makifbaysal/tasktrooper1172026년 10월 18일 업데이트

How to work a task returned with review, QA or UAT findings. Use when a task is in need_revision or PR review comments are in your context.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

makifbaysal/tasktrooper1172026년 10월 18일 업데이트

Use when the repository keeps its alerting in code, or a person asks for alerts outside TaskTrooper - PrometheusRule YAML, a Cloud Monitoring alert policy in Terraform, a Datadog monitor, each routed to TaskTrooper's alert ingest URL so it opens an incident here

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

makifbaysal/tasktrooper1172026년 10월 18일 업데이트

makifbaysal의 스킬 모두 보기

이 스킬의 문제 신고