Marketplace

express-api-generator

Generates Express.js API routes with proper middleware, error handling, validation, and TypeScript support. Use when creating REST APIs or Express endpoints.

$ 安裝

git clone https://github.com/Dexploarer/claudius-skills /tmp/claudius-skills && cp -r /tmp/claudius-skills/examples/intermediate/framework-skills/express-api-generator ~/.claude/skills/claudius-skills

// tip: Run this command in your terminal to install the skill


name: express-api-generator description: Generates Express.js API routes with proper middleware, error handling, validation, and TypeScript support. Use when creating REST APIs or Express endpoints.

Express.js API Generator Skill

Expert at creating well-structured Express.js APIs with TypeScript, proper error handling, and best practices.

When to Activate

  • "create Express API for [resource]"
  • "generate REST endpoints for [feature]"
  • "build Express routes for [entity]"
  • "scaffold Express API"

Complete API Structure

1. Router File

// routes/users.routes.ts
import { Router } from 'express';
import { UserController } from '../controllers/user.controller';
import { validate } from '../middleware/validation.middleware';
import { authenticate } from '../middleware/auth.middleware';
import { createUserSchema, updateUserSchema } from '../schemas/user.schema';

const router = Router();
const userController = new UserController();

// GET /api/users - List all users
router.get(
  '/',
  authenticate,
  userController.getAll
);

// GET /api/users/:id - Get user by ID
router.get(
  '/:id',
  authenticate,
  userController.getById
);

// POST /api/users - Create new user
router.post(
  '/',
  validate(createUserSchema),
  userController.create
);

// PUT /api/users/:id - Update user
router.put(
  '/:id',
  authenticate,
  validate(updateUserSchema),
  userController.update
);

// DELETE /api/users/:id - Delete user
router.delete(
  '/:id',
  authenticate,
  userController.delete
);

export default router;

2. Controller

// controllers/user.controller.ts
import { Request, Response, NextFunction } from 'express';
import { UserService } from '../services/user.service';
import { CreateUserDTO, UpdateUserDTO } from '../dto/user.dto';
import { ApiError } from '../utils/ApiError';
import { asyncHandler } from '../utils/asyncHandler';

export class UserController {
  private userService: UserService;

  constructor() {
    this.userService = new UserService();
  }

  getAll = asyncHandler(async (req: Request, res: Response) => {
    const { page = 1, limit = 10, search } = req.query;

    const result = await this.userService.getAll({
      page: Number(page),
      limit: Number(limit),
      search: search as string,
    });

    res.status(200).json({
      success: true,
      data: result.users,
      meta: {
        page: result.page,
        limit: result.limit,
        total: result.total,
        totalPages: Math.ceil(result.total / result.limit),
      },
    });
  });

  getById = asyncHandler(async (req: Request, res: Response) => {
    const { id } = req.params;

    const user = await this.userService.getById(id);

    if (!user) {
      throw new ApiError(404, 'User not found');
    }

    res.status(200).json({
      success: true,
      data: user,
    });
  });

  create = asyncHandler(async (req: Request, res: Response) => {
    const userData: CreateUserDTO = req.body;

    const user = await this.userService.create(userData);

    res.status(201).json({
      success: true,
      data: user,
      message: 'User created successfully',
    });
  });

  update = asyncHandler(async (req: Request, res: Response) => {
    const { id } = req.params;
    const userData: UpdateUserDTO = req.body;

    const user = await this.userService.update(id, userData);

    if (!user) {
      throw new ApiError(404, 'User not found');
    }

    res.status(200).json({
      success: true,
      data: user,
      message: 'User updated successfully',
    });
  });

  delete = asyncHandler(async (req: Request, res: Response) => {
    const { id } = req.params;

    await this.userService.delete(id);

    res.status(200).json({
      success: true,
      message: 'User deleted successfully',
    });
  });
}

3. Service Layer

// services/user.service.ts
import { User } from '../models/user.model';
import { CreateUserDTO, UpdateUserDTO } from '../dto/user.dto';
import { ApiError } from '../utils/ApiError';
import bcrypt from 'bcrypt';

interface GetAllOptions {
  page: number;
  limit: number;
  search?: string;
}

export class UserService {
  async getAll(options: GetAllOptions) {
    const { page, limit, search } = options;
    const skip = (page - 1) * limit;

    const query = search
      ? { $or: [
          { name: { $regex: search, $options: 'i' } },
          { email: { $regex: search, $options: 'i' } },
        ]}
      : {};

    const [users, total] = await Promise.all([
      User.find(query).skip(skip).limit(limit).select('-password'),
      User.countDocuments(query),
    ]);

    return { users, total, page, limit };
  }

  async getById(id: string) {
    const user = await User.findById(id).select('-password');
    return user;
  }

  async create(userData: CreateUserDTO) {
    const existingUser = await User.findOne({ email: userData.email });

    if (existingUser) {
      throw new ApiError(409, 'Email already exists');
    }

    const hashedPassword = await bcrypt.hash(userData.password, 10);

    const user = await User.create({
      ...userData,
      password: hashedPassword,
    });

    const userObject = user.toObject();
    delete userObject.password;

    return userObject;
  }

  async update(id: string, userData: UpdateUserDTO) {
    if (userData.password) {
      userData.password = await bcrypt.hash(userData.password, 10);
    }

    const user = await User.findByIdAndUpdate(
      id,
      { $set: userData },
      { new: true, runValidators: true }
    ).select('-password');

    return user;
  }

  async delete(id: string) {
    const user = await User.findByIdAndDelete(id);

    if (!user) {
      throw new ApiError(404, 'User not found');
    }

    return true;
  }
}

4. Validation Schema

// schemas/user.schema.ts
import Joi from 'joi';

export const createUserSchema = Joi.object({
  name: Joi.string().min(2).max(100).required(),
  email: Joi.string().email().required(),
  password: Joi.string().min(8).required(),
  role: Joi.string().valid('user', 'admin').default('user'),
});

export const updateUserSchema = Joi.object({
  name: Joi.string().min(2).max(100),
  email: Joi.string().email(),
  password: Joi.string().min(8),
  role: Joi.string().valid('user', 'admin'),
}).min(1);

5. Middleware

// middleware/validation.middleware.ts
import { Request, Response, NextFunction } from 'express';
import { Schema } from 'joi';
import { ApiError } from '../utils/ApiError';

export const validate = (schema: Schema) => {
  return (req: Request, res: Response, next: NextFunction) => {
    const { error, value } = schema.validate(req.body, {
      abortEarly: false,
      stripUnknown: true,
    });

    if (error) {
      const message = error.details.map(d => d.message).join(', ');
      throw new ApiError(400, message);
    }

    req.body = value;
    next();
  };
};
// middleware/auth.middleware.ts
import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';
import { ApiError } from '../utils/ApiError';

interface JwtPayload {
  userId: string;
  email: string;
}

declare global {
  namespace Express {
    interface Request {
      user?: JwtPayload;
    }
  }
}

export const authenticate = (req: Request, res: Response, next: NextFunction) => {
  const token = req.headers.authorization?.replace('Bearer ', '');

  if (!token) {
    throw new ApiError(401, 'Authentication required');
  }

  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET!) as JwtPayload;
    req.user = decoded;
    next();
  } catch (error) {
    throw new ApiError(401, 'Invalid or expired token');
  }
};

6. Error Handler

// utils/ApiError.ts
export class ApiError extends Error {
  constructor(
    public statusCode: number,
    public message: string,
    public errors: any[] = []
  ) {
    super(message);
    this.name = 'ApiError';
  }
}

// utils/asyncHandler.ts
import { Request, Response, NextFunction } from 'express';

export const asyncHandler = (fn: Function) => {
  return (req: Request, res: Response, next: NextFunction) => {
    Promise.resolve(fn(req, res, next)).catch(next);
  };
};

// middleware/errorHandler.ts
import { Request, Response, NextFunction } from 'express';
import { ApiError } from '../utils/ApiError';

export const errorHandler = (
  err: Error,
  req: Request,
  res: Response,
  next: NextFunction
) => {
  if (err instanceof ApiError) {
    return res.status(err.statusCode).json({
      success: false,
      message: err.message,
      errors: err.errors,
    });
  }

  console.error('Unexpected error:', err);

  res.status(500).json({
    success: false,
    message: 'Internal server error',
  });
};

File Structure

src/
├── routes/
│   └── user.routes.ts
├── controllers/
│   └── user.controller.ts
├── services/
│   └── user.service.ts
├── models/
│   └── user.model.ts
├── dto/
│   └── user.dto.ts
├── schemas/
│   └── user.schema.ts
├── middleware/
│   ├── auth.middleware.ts
│   ├── validation.middleware.ts
│   └── errorHandler.ts
└── utils/
    ├── ApiError.ts
    └── asyncHandler.ts

Best Practices

  • ✅ Separate routes, controllers, and services
  • ✅ Use TypeScript for type safety
  • ✅ Implement proper error handling
  • ✅ Validate input data
  • ✅ Use async/await with error handling
  • ✅ Implement authentication/authorization
  • ✅ Return consistent response format
  • ✅ Add pagination for list endpoints
  • ✅ Use HTTP status codes correctly
  • ✅ Handle edge cases
  • ✅ Add request logging
  • ✅ Implement rate limiting

Output Checklist

  • ✅ Routes file created
  • ✅ Controller implemented
  • ✅ Service layer added
  • ✅ Validation schemas defined
  • ✅ Middleware configured
  • ✅ Error handling setup
  • ✅ Tests created
  • 📝 API documentation provided

Repository

Dexploarer
Dexploarer
Author
Dexploarer/claudius-skills/examples/intermediate/framework-skills/express-api-generator
1
Stars
1
Forks
Updated3d ago
Added1w ago