create-resource-service
について
このClaude Skillは、CRUD操作、認可、およびイベント発行を扱うドメインエンティティ用のTypeScriptリソースサービスを生成します。ユーザーやノートなどのエンティティ向けに、BaseServiceを拡張しリポジトリと統合するサービスが必要な場合にご利用ください。データアクセスと権限チェックに関するプロジェクト規約に従った構造化されたサービスファイルを作成します。
クイックインストール
Claude Code
推奨/plugin add https://github.com/majiayu000/claude-skill-registrygit clone https://github.com/majiayu000/claude-skill-registry.git ~/.claude/skills/create-resource-serviceこのコマンドをClaude Codeにコピー&ペーストしてスキルをインストールします
ドキュメント
Create Resource Service
Creates a service for CRUD operations on a domain entity. Resource services extend BaseService for event emission, inject repositories for data access, and use AuthorizationService for permission checks.
Quick Reference
Location: src/services/{entity-name}.service.ts
Naming: Singular, kebab-case (e.g., note.service.ts, course.service.ts)
Prerequisites
Before creating a resource service, ensure you have:
- Schema created (
src/schemas/{entity-name}.schema.ts) - Repository interface created (
src/repositories/{entity-name}.repository.ts) - At least one repository implementation (MockDB or MongoDB)
Instructions
Step 1: Create the Service File
Create src/services/{entity-name}.service.ts
Step 2: Import Dependencies
import type { I{Entity}Repository } from "@/repositories/{entity-name}.repository";
import type { PaginatedResultType } from "@/schemas/shared.schema";
import type {
Create{Entity}Type,
{Entity}QueryParamsType,
{Entity}Type,
Update{Entity}Type,
} from "@/schemas/{entity-name}.schema";
import type { AuthenticatedUserContextType } from "@/schemas/user.schemas";
import { AuthorizationService } from "@/services/authorization.service";
import { UnauthorizedError } from "@/errors";
import { MockDb{Entity}Repository } from "@/repositories/mockdb/{entity-name}.mockdb.repository";
import { BaseService } from "@/events/base.service";
Step 3: Create the Service Class
export class {Entity}Service extends BaseService {
private readonly {entity}Repository: I{Entity}Repository;
private readonly authorizationService: AuthorizationService;
constructor(
{entity}Repository?: I{Entity}Repository,
authorizationService?: AuthorizationService,
) {
super("{entities}"); // Service name for events (plural)
this.{entity}Repository = {entity}Repository ?? new MockDb{Entity}Repository();
this.authorizationService = authorizationService ?? new AuthorizationService();
}
// CRUD methods...
}
Step 4: Implement CRUD Methods
getAll
async getAll(
params: {Entity}QueryParamsType,
user: AuthenticatedUserContextType,
): Promise<PaginatedResultType<{Entity}Type>> {
// Admins see all, users see only their own
if (this.authorizationService.isAdmin(user)) {
return this.{entity}Repository.findAll(params);
}
return this.{entity}Repository.findAll({ ...params, createdBy: user.userId });
}
getById
async getById(
id: string,
user: AuthenticatedUserContextType,
): Promise<{Entity}Type | null> {
const {entity} = await this.{entity}Repository.findById(id);
if (!{entity}) {
return null;
}
const canView = await this.authorizationService.canView{Entity}(user, {entity});
if (!canView) throw new UnauthorizedError();
return {entity};
}
create
async create(
data: Create{Entity}Type,
user: AuthenticatedUserContextType,
): Promise<{Entity}Type> {
const canCreate = await this.authorizationService.canCreate{Entity}(user);
if (!canCreate) throw new UnauthorizedError();
const {entity} = await this.{entity}Repository.create(data, user.userId);
this.emitEvent("created", {entity}, {
id: {entity}.id,
user,
});
return {entity};
}
update
async update(
id: string,
data: Update{Entity}Type,
user: AuthenticatedUserContextType,
): Promise<{Entity}Type | null> {
const {entity} = await this.{entity}Repository.findById(id);
if (!{entity}) {
return null;
}
const canUpdate = await this.authorizationService.canUpdate{Entity}(user, {entity});
if (!canUpdate) throw new UnauthorizedError();
const updated{Entity} = await this.{entity}Repository.update(id, data);
if (!updated{Entity}) {
return null;
}
this.emitEvent("updated", updated{Entity}, {
id: updated{Entity}.id,
user,
});
return updated{Entity};
}
delete
async delete(
id: string,
user: AuthenticatedUserContextType,
): Promise<boolean> {
const {entity} = await this.{entity}Repository.findById(id);
if (!{entity}) {
return false;
}
const canDelete = await this.authorizationService.canDelete{Entity}(user, {entity});
if (!canDelete) throw new UnauthorizedError();
const deleted = await this.{entity}Repository.remove(id);
if (deleted) {
this.emitEvent("deleted", {entity}, {
id: {entity}.id,
user,
});
}
return deleted;
}
Patterns & Rules
Extending BaseService
export class {Entity}Service extends BaseService {
constructor(...) {
super("{entities}"); // Plural name for event namespace
}
}
The serviceName is used for event routing (e.g., notes:created, notes:updated).
Dependency Injection
constructor(
{entity}Repository?: I{Entity}Repository,
authorizationService?: AuthorizationService,
) {
// Provide defaults for convenience, but allow injection for testing
this.{entity}Repository = {entity}Repository ?? new MockDb{Entity}Repository();
this.authorizationService = authorizationService ?? new AuthorizationService();
}
- Accept interfaces for repositories (not concrete classes)
- Provide defaults for easier instantiation
- Allow injection for testing with mocks
Authorization Pattern
Every operation should check permissions:
const canDoX = await this.authorizationService.canX{Entity}(user, {entity});
if (!canDoX) throw new UnauthorizedError();
You must add corresponding methods to AuthorizationService:
canView{Entity}(user, entity)canCreate{Entity}(user)canUpdate{Entity}(user, entity)canDelete{Entity}(user, entity)
Event Emission Pattern
Emit events after successful operations:
this.emitEvent("created", {entity}, { id: {entity}.id, user });
this.emitEvent("updated", updated{Entity}, { id: updated{Entity}.id, user });
this.emitEvent("deleted", {entity}, { id: {entity}.id, user });
Events are only emitted for create, update, delete - not for reads.
Error Handling
- Not found: Return
null(let controller decide HTTP status) - Unauthorized: Throw
UnauthorizedErrorfrom@/errors - Other errors: Let them propagate (global error handler catches)
Return Types
getAll:Promise<PaginatedResultType<{Entity}Type>>getById:Promise<{Entity}Type | null>create:Promise<{Entity}Type>update:Promise<{Entity}Type | null>delete:Promise<boolean>
Complete Example
See REFERENCE.md for a complete NoteService implementation.
After Creating the Service
- Add authorization methods to
AuthorizationServicefor this entity - Add event schema (optional) - see
add-resource-eventsskill - Create controller - see
create-controllerskill - Write tests - see
test-serviceskill
What NOT to Do
- Do NOT put HTTP-specific logic in services (that's for controllers)
- Do NOT return HTTP status codes or responses
- Do NOT skip authorization checks
- Do NOT emit events before confirming the operation succeeded
- Do NOT inject concrete repository classes - use interfaces
GitHub リポジトリ
関連スキル
content-collections
メタThis skill provides a production-tested setup for Content Collections, a TypeScript-first tool that transforms Markdown/MDX files into type-safe data collections with Zod validation. Use it when building blogs, documentation sites, or content-heavy Vite + React applications to ensure type safety and automatic content validation. It covers everything from Vite plugin configuration and MDX compilation to deployment optimization and schema validation.
evaluating-llms-harness
テストThis Claude Skill runs the lm-evaluation-harness to benchmark LLMs across 60+ standardized academic tasks like MMLU and GSM8K. It's designed for developers to compare model quality, track training progress, or report academic results. The tool supports various backends including HuggingFace and vLLM models.
sglang
メタSGLang is a high-performance LLM serving framework that specializes in fast, structured generation for JSON, regex, and agentic workflows using its RadixAttention prefix caching. It delivers significantly faster inference, especially for tasks with repeated prefixes, making it ideal for complex, structured outputs and multi-turn conversations. Choose SGLang over alternatives like vLLM when you need constrained decoding or are building applications with extensive prefix sharing.
polymarket
メタThis skill enables developers to build applications with the Polymarket prediction markets platform, including API integration for trading and market data. It also provides real-time data streaming via WebSocket to monitor live trades and market activity. Use it for implementing trading strategies or creating tools that process live market updates.
