ワンクリックで
new-service
Scaffold a service class for a given domain following project conventions
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Scaffold a service class for a given domain following project conventions
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
Implement a numbered testing strategy phase from docs/testing_strategy_phases/phase_$ARGUMENTS_*.md step by step
Implement a numbered phase from docs/phases/phase-$ARGUMENTS.md step by step
Review changed code for correctness, security, and project conventions. Invoke this skill automatically whenever a complete unit of work is done — a full implementation phase, a complete feature (repository + service + route + component all connected), or just before creating a PR. Do NOT invoke after editing a single file or mid-way through a feature. If the user says "done", "finished", "phase complete", "ready to commit", or similar, trigger this review immediately.
Run prisma migrate dev, regenerate client to the correct output path, and verify import paths
Scaffold a Prisma-backed repository for a given Prisma model
Final pre-commit check: run type-check (npx tsc --noEmit), lint (npm run lint), and tests (npm test)
| name | new-service |
| description | Scaffold a service class for a given domain following project conventions |
| disable-model-invocation | true |
| argument-hint | [domain-name] |
Scaffold a service for the $ARGUMENTS domain.
src/services/$ARGUMENTS.service.tssrc/types/index.ts (add alongside existing schemas)import { ${camelCase}Repository } from '@/repositories/$ARGUMENTS.repository'
import { createInputSchema, type CreateInput } from '@/types'
export const ${camelCase}Service = {
async create(userId: string, input: CreateInput) {
createInputSchema.parse(input) // validate first
return ${camelCase}Repository.create({ ...input, userId })
},
async getById(id: string, userId: string) {
const record = await ${camelCase}Repository.findById(id)
if (!record) throw new Error('Not found')
if (record.userId !== userId) throw new Error('Unauthorized') // AFTER fetch
return record
},
async listForUser(userId: string) {
return ${camelCase}Repository.findMany({ userId })
},
async update(id: string, userId: string, input: CreateInput) {
createInputSchema.parse(input)
const existing = await ${camelCase}Repository.findById(id)
if (!existing) throw new Error('Not found')
if (existing.userId !== userId) throw new Error('Unauthorized')
return ${camelCase}Repository.update(id, input)
},
async delete(id: string, userId: string) {
const existing = await ${camelCase}Repository.findById(id)
if (!existing) throw new Error('Not found')
if (existing.userId !== userId) throw new Error('Unauthorized')
return ${camelCase}Repository.delete(id)
},
}
The check goes AFTER the fetch, not before. This is intentional:
'Not found' (becomes 404)'Unauthorized' (becomes 403)
Checking before the fetch would collapse both cases into 403 and leak that a record exists.userId flows in as a parameterServices do NOT call authService.getUser() internally. The API route retrieves the user from the session and passes userId into every service method. This keeps services testable in isolation.
@/repositories/...@/typesprisma directly — that is the repository's job@prisma/clientDefine in src/types/index.ts:
export const createInvoiceSchema = z.object({ ... })
export type CreateInvoiceInput = z.infer<typeof createInvoiceSchema>
npx tsc --noEmit