一键导入
edit-backend
Contains context and guidelines for editing the backend codebase, including creating new entities, services, routes, dependency injection, and testing.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Contains context and guidelines for editing the backend codebase, including creating new entities, services, routes, dependency injection, and testing.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
| name | edit-backend |
| description | Contains context and guidelines for editing the backend codebase, including creating new entities, services, routes, dependency injection, and testing. |
This file describes the architectural principles, conventions, and patterns of this codebase. Follow them strictly when adding or modifying code.
any)dotenv-safebcryptnodemailer + eta templates + inline-cssdayjsdayjs)zod if validation library is needed)Feature-based layout — each feature owns all its related files:
src/
auth/ # Authentication feature: guard, hasher, routes, service, session store
db/ # Database connection provider and schema aggregation
mailer/ # Mail provider interface + implementation, mail service
users/ # User entity, repository interface + SQL implementation, table schema
utils/ # Pure, context-free utility functions only
config.ts # Config loading via dotenv-safe
deps.ts # Dependency wiring — single application container
errors.ts # AppError class + ErrorCode enum
httpProvider.ts # HTTP client provider
logger.ts # Logger interface + ConsoleLogger implementation
server.ts # Fastify server setup
index.ts # Entry point
test/ # Mirrors src/ structure; only *.test.ts files run as tests
Do not use a Rails-style layout (controllers/, models/, views/, etc.).
Do not pollute utils/ with anything that is feature-specific or has business context.
All dependencies are manually injected via constructors. There is no IoC library.
src/deps.ts is the single wiring point — it instantiates every class and returns them in one object (ApplicationContainer). This object is your whole application.
// src/deps.ts
export function createApplicationContainer() {
const config = loadAppConfig();
const logger = new ConsoleLogger();
const dbProvider = new DBProvider(config);
// ...
const authService = new AuthService(authHasher, authSessionStore, usersRepository, mailerService, logger);
return { config, logger, dbProvider, authService, ... };
}
export type ApplicationContainer = ReturnType<typeof createApplicationContainer>;
Rules:
createApplicationContainer.| Layer | Convention | Example |
|---|---|---|
| Persistence (DB access via ORM) | *Repository | UsersRepository |
| Custom DB queries | *Gateway | UsersGateway |
| External API / infrastructure | *Provider | MailerProvider, HttpProvider |
| Business logic | *Service | AuthService |
| HTTP routes | make*Routes function | makeAuthRoutes |
Entities are plain TypeScript interfaces — no classes, no decorators, no ORM types.
// src/users/userEntity.ts
export interface User {
id: string;
email: string;
password: string;
createdAt: Date;
}
$inferSelect from Drizzle or any ORM type as your entity type.usersTable) separate from the entity interface.Table schemas live alongside their feature, separate from entities:
// src/users/usersTable.ts
export const usersTable = pgTable('users', {
id: uuid('id').primaryKey(),
email: varchar('email', { length: 128 }).notNull().unique(),
password: varchar('password', { length: 128 }).notNull(),
createdAt: timestamp('created_at', {
withTimezone: true,
mode: 'date',
}).notNull(),
});
Rules:
makeEntityId() from src/utils/id.ts in the application layer, never auto-incremented by the DB.NOT NULL, UNIQUE, foreign keys) as a last line of defense, but always enforce the same rules in application code first.Always generate IDs in the application layer:
import { makeEntityId } from '../utils/id';
const user: User = {
id: makeEntityId(), // crypto.randomUUID()
email,
password: hashedPassword,
createdAt: new Date(),
};
Never rely on database sequences or auto-increment for primary keys.
Use AppError with a fixed ErrorCode enum. Never create custom error subclasses.
// Throw immediately when a rule is violated
if (!user) {
throw new AppError(
ErrorCode.INVALID_CREDENTIALS,
'Invalid email or password',
);
}
if (order.creatorId !== user.id) {
throw new AppError(
ErrorCode.NOT_ENOUGH_PERMISSIONS,
'Only order creator can cancel the order!',
);
}
Available error codes can be read from src/errors.ts.
The Fastify error handler in server.ts catches all AppError instances and returns:
{ "code": "NOT_FOUND", "message": "...", "timestamp": 1746125890728 }
Config is loaded once in loadAppConfig() via dotenv-safe, which validates all required env vars against .env.example. The resulting AppConfig object is passed through DI.
Do not read process.env anywhere outside of src/config.ts.
AuthSessionStore interface abstracts session storage (default: in-memory InMemoryAuthSessionStore). It can be replaced with Redis or DB without changing any consumer.AuthHasher interface abstracts password hashing (default: bcrypt via BcryptHasher).auth_session (httpOnly, path /).AuthGuard) is used as a Fastify preHandler.FastifyPluginCallback, registered via the ApplicationContainer.server.ts only.any is forbidden in ESLint config. Use unknown for truly unknown input, and Record<K, V> for object shapes.@ts-ignore only as a last resort with a justification comment.*.test.ts in test/ mirroring the src/ structure.// test/auth/authMisc.ts
export const mockAuthHasher: AuthHasher = {
comparePassword: async (password, hashedPassword) =>
password === hashedPassword,
hashPassword: async (password) => password,
};
const service = new AuthService(
mockAuthHasher,
mockAuthSessionStore,
createMockUserRepo([]),
mockMailerService,
mockLogger,
);
'password strength check is correct''should check password strength correctly'src/<feature>/.interface in <feature>Entity.ts.<feature>Table.ts.<feature>Repository.ts (or <feature>Provider.ts).Sql<Feature>Repository) in the same file.<feature>Service.ts, depending only on interfaces.<feature>Routes.ts using make<Feature>Routes(app) pattern.src/deps.ts inside createApplicationContainer.src/server.ts.test/<feature>/<feature>Service.test.ts.| Topic | Forbidden |
|---|---|
| Web framework | Express, NestJS, Hono |
| ORM | TypeORM, Sequelize, Prisma |
| Auth | JWT, Passport, Firebase Auth |
| DB primary keys | Auto-increment / DB sequences |
| DB logic | Stored procedures, triggers, functions |
| Error handling | Custom error subclasses per case |
| Types | any, untyped object |
| Config | Reading process.env outside config.ts |
| Fastify plugins | Auth, DB, Redis, JWT in middleware |
utils/ | Feature-specific or business-context helpers |
| Tests | Jest, Mocha, Chai; "should" wording |
| Packages | lodash, moment, class-validator, passport |