/nodejs-backend
Node.js backend patterns with Express/Fastify, repositories
$ npx -y skills add alinaqi/claude-bootstrap --skill nodejs-backend --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/nodejs-backend
Context preview
The summary Claude sees to decide when to auto-load this skill.
Node.js backend patterns with Express/Fastify, repositories
SKILL.md
nodejs-backend.SKILL.mdname: nodejs-backend
description: Node.js backend patterns with Express/Fastify, repositories
when-to-use: When working on Node.js backend code - API routes, middleware, server setup
user-invocable: false
paths: ["src/api/**", "src/routes/**", "src/server/**", "src/middleware/**", "server/**", "api/**"]
effort: medium
Node.js Backend Skill
---
Project Structure
project/
├── src/
│ ├── core/ # Pure business logic
│ │ ├── types.ts # Domain types
│ │ ├── errors.ts # Domain errors
│ │ └── services/ # Pure functions
│ │ ├── user.ts
│ │ └── order.ts
│ ├── infra/ # Side effects
│ │ ├── http/ # HTTP layer
│ │ │ ├── server.ts # Server setup
│ │ │ ├── routes/ # Route handlers
│ │ │ └── middleware/ # Express middleware
│ │ ├── db/ # Database
│ │ │ ├── client.ts # DB connection
│ │ │ ├── repositories/ # Data access
│ │ │ └── migrations/ # Schema migrations
│ │ └── external/ # Third-party APIs
│ ├── config/ # Configuration
│ │ └── index.ts # Env vars, validated
│ └── index.ts # Entry point
├── tests/
│ ├── unit/
│ └── integration/
├── package.json
└── CLAUDE.md
---
API Design
Route Handler Pattern
// routes/users.ts
import { Router } from 'express';
import { z } from 'zod';
import { createUser } from '../../core/services/user';
import { UserRepository } from '../db/repositories/user';
const CreateUserSchema = z.object({
email: z.string().email(),
name: z.string().min(1).max(100),
});
export function createUserRoutes(userRepo: UserRepository): Router {
const router = Router();
router.post('/', async (req, res, next) => {
try {
const input = CreateUserSchema.parse(req.body);
const user = await createUser(input, userRepo);
res.status(201).json(user);
} catch (error) {
next(error);
}
});
return router;
}Dependency Injection at Composition Root
// index.ts
import { createApp } from './infra/http/server';
import { createDbClient } from './infra/db/client';
import { UserRepository } from './infra/db/repositories/user';
import { createUserRoutes } from './infra/http/routes/users';
async function main(): Promise<void> {
const db = await createDbClient();
const userRepo = new UserRepository(db);
const app = createApp({
userRoutes: createUserRoutes(userRepo),
});
app.listen(3000);
}---
Error Handling
Domain Errors
// core/errors.ts
export class DomainError extends Error {
constructor(
message: string,
public readonly code: string,
public readonly statusCode: number = 400
) {
super(message);
this.name = 'DomainError';
}
}
export class NotFoundError extends DomainError {
constructor(resource: string, id: string) {
super(`${resource} with id ${id} not found`, 'NOT_FOUND', 404);
}
}
export class ValidationError extends DomainError {
constructor(message: string) {
super(message, 'VALIDATION_ERROR', 400);
}
}Global Error Handler
// middleware/errorHandler.ts
import { ErrorRequestHandler } from 'express';
import { DomainError } from '../../core/errors';
import { ZodError } from 'zod';
export const errorHandler: ErrorRequestHandler = (err, req, res, next) => {
if (err instanceof DomainError) {
return res.status(err.statusCode).json({
error: { code: err.code, message: err.message },
});
}
if (err instanceof ZodError) {
return res.status(400).json({
error: { code: 'VALIDATION_ERROR', details: err.errors },
});
}
console.error('Unexpected error:', err);
return res.status(500).json({
error: { code: 'INTERNAL_ERROR', message: 'Something went wrong' },
});
};---
Database Patterns
Repository Pattern
// db/repositories/user.ts
import { Kysely } from 'kysely';
import { Database, User } from '../types';
export class UserRepository {
constructor(private db: Kysely<Database>) {}
async findById(id: string): Promise<User | null> {
return this.db
.selectFrom('users')
.where('id', '=', id)
.selectAll()
.executeTakeFirst() ?? null;
}
async create(data: Omit<User, 'id' | 'createdAt'>): Promise<User> {
return this.db
.insertInto('users')
.values(data)
.returningAll()
.executeTakeFirstOrThrow();
}
}Transactions
async function transferFunds(
fromId: string,
toId: string,
amount: number,
db: Kysely<Database>
): Promise<void> {
await db.transaction().execute(async (trx) => {
await trx
.updateTable('accounts')
.set((eb) => ({ balance: eb('balance', '-', amount) }))
.where('id', '=', fromId)
.execute();
await trx
.updateTable('accounts')
.set((eb) => ({ balance: eb('balance', '+', amount) }))
.where('id', '=', toId)
.execute();
});
}---
Configuration
Validated Config
// config/index.ts
import { z } from 'zod';
const ConfigSchema = z.object({
NODE_ENV: z.enum(['development', 'production', 'test']),
PORT: z.coerce.number().default(3000),
DATABASE_URL: z.string().url(),
API_KEY: z.string().min(1),
});
export type Config = z.infer<typeof ConfigSchema>;
export function loadConfig(): Config {
return ConfigSchema.parse(process.env);
}---
Testing
Unit Tests (Core)
// tests/unit/services/user.test.ts
import { createUser } from '../../../src/core/services/user';
describe('createUser', () => {
it('creates user with valid data', async () => {
const mockRepo = {
create: jest.fn().mockResolvedValue({ id: '1', email: 'test@example.com' }),
findByEmail: jest.fn().mockResolvedValue(null),
};
const result = await createUser(Read more
name: nodejs-backend description: Node.js backend patterns with Express/Fastify, repositories when-to-use: When working on Node.js backend code - API routes, middleware, server setup user-invocable: false paths: ["src/api/**", "src/routes/**", "src/server/**", "src/middleware/**", "server/**", "api/**"] effort: medium
Node.js Backend Skill
---
Project Structure
project/ ├── src/ │ ├── core/ # Pure business logic │ │ ├── types.ts # Domain types │ │ ├── errors.ts # Domain errors │ │ └── services/ # Pure functions │ │ ├── user.ts │ │ └── order.ts │ ├── infra/ # Side effects │ │ ├── http/ # HTTP layer │ │ │ ├── server.ts # Server setup │ │ │ ├── routes/ # Route handlers │ │ │ └── middleware/ # Express middleware │ │ ├── db/ # Database │ │ │ ├── client.ts # DB connection │ │ │ ├── repositories/ # Data access │ │ │ └── migrations/ # Schema migrations │ │ └── external/ # Third-party APIs │ ├── config/ # Configuration │ │ └── index.ts # Env vars, validated │ └── index.ts # Entry point ├── tests/ │ ├── unit/ │ └── integration/ ├── package.json └── CLAUDE.md
---
API Design
Route Handler Pattern
// routes/users.ts
import { Router } from 'express';
import { z } from 'zod';
import { createUser } from '../../core/services/user';
import { UserRepository } from '../db/repositories/user';
const CreateUserSchema = z.object({
email: z.string().email(),
name: z.string().min(1).max(100),
});
export function createUserRoutes(userRepo: UserRepository): Router {
const router = Router();
router.post('/', async (req, res, next) => {
try {
const input = CreateUserSchema.parse(req.body);
const user = await createUser(input, userRepo);
res.status(201).json(user);
} catch (error) {
next(error);
}
});
return router;
}Dependency Injection at Composition Root
// index.ts
import { createApp } from './infra/http/server';
import { createDbClient } from './infra/db/client';
import { UserRepository } from './infra/db/repositories/user';
import { createUserRoutes } from './infra/http/routes/users';
async function main(): Promise<void> {
const db = await createDbClient();
const userRepo = new UserRepository(db);
const app = createApp({
userRoutes: createUserRoutes(userRepo),
});
app.listen(3000);
}---
Error Handling
Domain Errors
// core/errors.ts
export class DomainError extends Error {
constructor(
message: string,
public readonly code: string,
public readonly statusCode: number = 400
) {
super(message);
this.name = 'DomainError';
}
}
export class NotFoundError extends DomainError {
constructor(resource: string, id: string) {
super(`${resource} with id ${id} not found`, 'NOT_FOUND', 404);
}
}
export class ValidationError extends DomainError {
constructor(message: string) {
super(message, 'VALIDATION_ERROR', 400);
}
}Global Error Handler
// middleware/errorHandler.ts
import { ErrorRequestHandler } from 'express';
import { DomainError } from '../../core/errors';
import { ZodError } from 'zod';
export const errorHandler: ErrorRequestHandler = (err, req, res, next) => {
if (err instanceof DomainError) {
return res.status(err.statusCode).json({
error: { code: err.code, message: err.message },
});
}
if (err instanceof ZodError) {
return res.status(400).json({
error: { code: 'VALIDATION_ERROR', details: err.errors },
});
}
console.error('Unexpected error:', err);
return res.status(500).json({
error: { code: 'INTERNAL_ERROR', message: 'Something went wrong' },
});
};---
Database Patterns
Repository Pattern
// db/repositories/user.ts
import { Kysely } from 'kysely';
import { Database, User } from '../types';
export class UserRepository {
constructor(private db: Kysely<Database>) {}
async findById(id: string): Promise<User | null> {
return this.db
.selectFrom('users')
.where('id', '=', id)
.selectAll()
.executeTakeFirst() ?? null;
}
async create(data: Omit<User, 'id' | 'createdAt'>): Promise<User> {
return this.db
.insertInto('users')
.values(data)
.returningAll()
.executeTakeFirstOrThrow();
}
}Transactions
async function transferFunds(
fromId: string,
toId: string,
amount: number,
db: Kysely<Database>
): Promise<void> {
await db.transaction().execute(async (trx) => {
await trx
.updateTable('accounts')
.set((eb) => ({ balance: eb('balance', '-', amount) }))
.where('id', '=', fromId)
.execute();
await trx
.updateTable('accounts')
.set((eb) => ({ balance: eb('balance', '+', amount) }))
.where('id', '=', toId)
.execute();
});
}---
Configuration
Validated Config
// config/index.ts
import { z } from 'zod';
const ConfigSchema = z.object({
NODE_ENV: z.enum(['development', 'production', 'test']),
PORT: z.coerce.number().default(3000),
DATABASE_URL: z.string().url(),
API_KEY: z.string().min(1),
});
export type Config = z.infer<typeof ConfigSchema>;
export function loadConfig(): Config {
return ConfigSchema.parse(process.env);
}---
Testing
Unit Tests (Core)
// tests/unit/services/user.test.ts
import { createUser } from '../../../src/core/services/user';
describe('createUser', () => {
it('creates user with valid data', async () => {
const mockRepo = {
create: jest.fn().mockResolvedValue({ id: '1', email: 'test@example.com' }),
findByEmail: jest.fn().mockResolvedValue(null),
};
const result = await createUser(Turn Claude Code into a self-reviewing, test-enforced engineering system that remembers context across sessions — then route work across 13 models from a single dashboard.
Repo: alinaqi/claude-bootstrap
Other skills on maggy.
- /aeo-optimization
AI Engine Optimization - semantic triples, page templates, content clusters for AI citations
Open skill - /agent-teams
Claude Code Agent Teams - default team-based development with strict TDD pipeline enforcement
Open skill - /agentic-development
Build AI agents with Pydantic AI (Python) and Claude SDK (Node.js)
Open skill - /ai-models
Latest AI models reference - Claude, OpenAI, Gemini, Eleven Labs, Replicate
Open skill - /android-java
Android Java development with MVVM, ViewBinding, and Espresso testing
Open skill - /android-kotlin
Android Kotlin development with Coroutines, Jetpack Compose, Hilt, and MockK testing
Open skill

