Skip to content
create-resource-service logo

Create Resource Service

create-resource-service

Create a resource service for CRUD operations on domain entities. Use when creating services for entities like notes, users, courses that need data operations, authorization, and event emission. Triggers on "resource service", "entity service", "crud service", "note service", "create service for".

SKILL.md

Full skill instructions

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:

  1. Schema created (src/​schemas/​{entity-name}.schema.ts)
  2. Repository interface created (src/​repositories/​{entity-name}.repository.ts)
  3. 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 UnauthorizedError from @/​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

  1. Add authorization methods to AuthorizationService for this entity
  2. Add event schema (optional) - see add-resource-events skill
  3. Create controller - see create-controller skill
  4. Write tests - see test-service skill

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

More Other skills

Delegate independent tasks to specialized agents working concurrently with isolated context.

295.4K 206.3K
View

Applies Anthropic's official brand colors and typography to any sort of artifact that may benefit from having Anthropic's look-and-feel. Use it when brand colors or style guidelines, visual formatting, or company design standards apply.

179.7K 100.1K
View

Toolkit for styling artifacts with a theme. These artifacts can be slides, docs, reportings, HTML landing pages, etc. There are 10 pre-set themes with colors/fonts that you can apply to any artifact that has been creating, or can generate a new theme on-the-fly.

179.7K 89.5K
View

A set of resources to help me write all kinds of internal communications, using the formats that my company likes to use. Claude should use this skill whenever asked to write some sort of internal communications (status reports, leadership updates, 3P updates, company newsletters, FAQs, incident reports, project updates, etc.).

179.7K 76.1K
View

Fundamental design principles for writing maintainable, testable Python code.

40.2K 21.9K
View

Modern responsive layouts using container queries, fluid typography, CSS Grid, and mobile-first strategies.

40.2K 20K
View

Generate Playwright tests from scenarios using interactive browser exploration and validation.

39.7K 18.2K
View

Modern React state management with Redux Toolkit, Zustand, Jotai, and React Query for every state category.

40.2K 14.7K
View

GDPR-compliant data handling with consent management, data subject rights, and privacy controls.

40.2K 14.3K
View

Python error handling patterns including input validation, exception hierarchies, and partial failure handling. Use when implementing validation logic, designing exception strategies, handling batch processing failures, or building robust APIs.

40.2K 13.5K
View

Detailed multi-file refactor planning with safety checks and rollback guidance.

39.7K 13K
View