Back to Skills

create-controller

majiayu000
Updated Today
1 views
58
9
58
View on GitHub
Metageneral

About

This skill generates HTTP controller files that handle request/response logic and connect routes to services. It creates TypeScript controllers with proper imports, method stubs, and validation based on existing schemas and services. Use it when you need to implement API endpoints after setting up your schemas and service layer.

Quick Install

Claude Code

Recommended
Plugin CommandRecommended
/plugin add https://github.com/majiayu000/claude-skill-registry
Git CloneAlternative
git clone https://github.com/majiayu000/claude-skill-registry.git ~/.claude/skills/create-controller

Copy and paste this command in Claude Code to install this skill

Documentation

Create Controller

Creates a controller that handles HTTP requests and responses. Controllers are thin layers that extract data from requests, call services, and return responses.

Quick Reference

Location: src/controllers/{entity-name}.controller.ts Naming: Singular, kebab-case (e.g., note.controller.ts, user.controller.ts)

Prerequisites

Before creating a controller, ensure you have:

  1. Schema created with request/response types (src/schemas/{entity-name}.schema.ts)
  2. Service created (src/services/{entity-name}.service.ts)

Instructions

Step 1: Create the Controller File

Create src/controllers/{entity-name}.controller.ts

Step 2: Import Dependencies

import type { Context } from "hono";
import { {Entity}Service } from "@/services/{entity-name}.service";
import type { EntityIdParamType } from "@/schemas/shared.schema";
import type {
  Create{Entity}Type,
  {Entity}QueryParamsType,
  Update{Entity}Type,
} from "@/schemas/{entity-name}.schema";
import type { AppEnv } from "@/schemas/app-env.schema";
import type { AuthenticatedUserContextType } from "@/schemas/user.schemas";
import { NotFoundError } from "@/errors";

Step 3: Create the Controller Class

export class {Entity}Controller {
  private {entity}Service: {Entity}Service;

  constructor({entity}Service?: {Entity}Service) {
    if ({entity}Service) {
      this.{entity}Service = {entity}Service;
    } else {
      this.{entity}Service = new {Entity}Service();
    }
  }

  // Handler methods...
}

Step 4: Implement CRUD Handlers

getAll

getAll = async (c: Context<AppEnv>): Promise<Response> => {
  const user = c.var.user as AuthenticatedUserContextType;
  const query = c.var.validatedQuery as {Entity}QueryParamsType;
  const {entities} = await this.{entity}Service.getAll(query, user);
  return c.json({entities});
};

getById

getById = async (c: Context<AppEnv>): Promise<Response> => {
  const user = c.var.user as AuthenticatedUserContextType;
  const { id } = c.var.validatedParams as EntityIdParamType;
  const {entity} = await this.{entity}Service.getById(id, user);
  if (!{entity}) throw new NotFoundError();
  return c.json({entity});
};

create

create = async (c: Context<AppEnv>): Promise<Response> => {
  const user = c.var.user as AuthenticatedUserContextType;
  const body = c.var.validatedBody as Create{Entity}Type;
  const {entity} = await this.{entity}Service.create(body, user);
  return c.json({entity});
};

update

update = async (c: Context<AppEnv>): Promise<Response> => {
  const user = c.var.user as AuthenticatedUserContextType;
  const { id } = c.var.validatedParams as EntityIdParamType;
  const body = c.var.validatedBody as Update{Entity}Type;
  const {entity} = await this.{entity}Service.update(id, body, user);
  if (!{entity}) throw new NotFoundError();
  return c.json({entity});
};

delete

delete = async (c: Context<AppEnv>): Promise<Response> => {
  const user = c.var.user as AuthenticatedUserContextType;
  const { id } = c.var.validatedParams as EntityIdParamType;
  const success = await this.{entity}Service.delete(id, user);
  if (!success) throw new NotFoundError();
  return c.json({ message: "{Entity} deleted successfully" });
};

Patterns & Rules

Handler Method Pattern

Use arrow functions assigned to class properties for handlers:

// Correct - arrow function maintains `this` binding
getAll = async (c: Context<AppEnv>): Promise<Response> => {
  // ...
};

// Wrong - regular method loses `this` when passed as callback
async getAll(c: Context<AppEnv>): Promise<Response> {
  // ...
}

Context Variables

Data is pre-validated by middleware and stored in c.var:

// User from auth middleware
const user = c.var.user as AuthenticatedUserContextType;

// Validated query params from validation middleware
const query = c.var.validatedQuery as {Entity}QueryParamsType;

// Validated request body from validation middleware
const body = c.var.validatedBody as Create{Entity}Type;

// Validated URL params from validation middleware
const { id } = c.var.validatedParams as EntityIdParamType;

Dependency Injection

Allow service injection for testing:

constructor({entity}Service?: {Entity}Service) {
  if ({entity}Service) {
    this.{entity}Service = {entity}Service;
  } else {
    this.{entity}Service = new {Entity}Service();
  }
}

Error Handling

Controllers throw domain errors - global error handler converts to HTTP:

// Service returns null for not found
const {entity} = await this.{entity}Service.getById(id, user);
if (!{entity}) throw new NotFoundError();

// Service throws UnauthorizedError for permission denied
// Let it propagate - global handler catches it

Response Format

Use c.json() for all responses:

// Return entity
return c.json({ entity });

// Return paginated result
return c.json({ entities }); // { data: [...], total: n, page: 1, ... }

// Return success message
return c.json({ message: "{Entity} deleted successfully" });

AppEnv Type

Always type Context with AppEnv:

import type { AppEnv } from "@/schemas/app-env.schema";

getAll = async (c: Context<AppEnv>): Promise<Response> => {
  // c.var is properly typed
};

The AppEnv interface provides types for:

  • c.var.user - Authenticated user context
  • c.var.validatedQuery - Validated query parameters
  • c.var.validatedBody - Validated request body
  • c.var.validatedParams - Validated URL parameters

Complete Example

See REFERENCE.md for a complete NoteController implementation.

What NOT to Do

  • Do NOT validate input in controllers (use validation middleware)
  • Do NOT access c.req.json() directly (use c.var.validatedBody)
  • Do NOT catch errors (let global error handler catch them)
  • Do NOT return HTTP status codes manually (use domain errors)
  • Do NOT put business logic in controllers (that's for services)
  • Do NOT use regular methods (use arrow functions for this binding)

See Also

  • create-routes - Wire controller handlers to routes with middleware
  • create-middleware - Create validation and auth middleware
  • test-controller - Test controller handlers

GitHub Repository

majiayu000/claude-skill-registry
Path: skills/create-controller

Related Skills

algorithmic-art

Meta

This Claude Skill creates original algorithmic art using p5.js with seeded randomness and interactive parameters. It generates .md files for algorithmic philosophies, plus .html and .js files for interactive generative art implementations. Use it when developers need to create flow fields, particle systems, or other computational art while avoiding copyright issues.

View skill

subagent-driven-development

Development

This skill executes implementation plans by dispatching a fresh subagent for each independent task, with code review between tasks. It enables fast iteration while maintaining quality gates through this review process. Use it when working on mostly independent tasks within the same session to ensure continuous progress with built-in quality checks.

View skill

executing-plans

Design

Use the executing-plans skill when you have a complete implementation plan to execute in controlled batches with review checkpoints. It loads and critically reviews the plan, then executes tasks in small batches (default 3 tasks) while reporting progress between each batch for architect review. This ensures systematic implementation with built-in quality control checkpoints.

View skill

cost-optimization

Other

This Claude Skill helps developers optimize cloud costs through resource rightsizing, tagging strategies, and spending analysis. It provides a framework for reducing cloud expenses and implementing cost governance across AWS, Azure, and GCP. Use it when you need to analyze infrastructure costs, right-size resources, or meet budget constraints.

View skill