| name | backend-api-patterns |
| description | Backend and API implementation patterns for scalability, security, and maintainability. Use when building APIs, services, and backend infrastructure. |
This skill provides backend and API implementation patterns for building robust, scalable services.
When to Invoke This Skill
Automatically activate for:
- API endpoint implementation
- Database operations and queries
- Authentication and authorization
- Caching and performance optimization
- Service architecture design
API Design Patterns
Consistent Response Structure
interface ApiResponse<T> {
data?: T;
error?: {
code: string;
message: string;
details?: Record<string, unknown>;
};
meta?: {
pagination?: {
page: number;
pageSize: number;
total: number;
totalPages: number;
};
timestamp?: string;
requestId?: string;
};
}
function success<T>(data: T, meta?: ApiResponse<T>['meta']): ApiResponse<T> {
return { data, meta };
}
function error(
code: string,
message: string,
details?: Record<string, unknown>
): ApiResponse<never> {
return { error: { code, message, details } };
}
function paginated<T>(
data: T[],
page: number,
pageSize: number,
total: number
): ApiResponse<T[]> {
return {
data,
meta: {
pagination: {
page,
pageSize,
total,
totalPages: Math.ceil(total / pageSize),
},
},
};
}
Route Handler Pattern
type Handler<T> = (
req: Request,
context: { params: Record<string, string> }
) => Promise<T>;
function createHandler<T>(handler: Handler<T>) {
return async (req: Request, context: { params: Record<string, string> }) => {
const requestId = crypto.randomUUID();
try {
const result = await handler(req, context);
return Response.json(success(result, { requestId }));
} catch (err) {
if (err instanceof AppError) {
return Response.json(
error(err.code, err.message),
{ status: err.statusCode }
);
}
console.error(`[${requestId}] Unexpected error:`, err);
return .(
(, ),
{ : }
);
}
};
}
= ( (req, { params }) => {
user = userService.(params.);
(!user) (, params.);
user;
});
Service Layer Pattern
Repository Pattern
interface Repository<T, ID = string> {
findById(id: ID): Promise<T | null>;
findMany(options: FindOptions<T>): Promise<T[]>;
count(filter?: Partial<T>): Promise<number>;
create(data: CreateInput<T>): Promise<T>;
update(id: ID, data: UpdateInput<T>): Promise<T>;
delete(id: ID): Promise<void>;
}
interface FindOptions<T> {
filter?: Partial<T>;
orderBy?: keyof T;
orderDir?: 'asc' | 'desc';
limit?: number;
offset?: number;
}
type CreateInput<T> = Omit<T, 'id' | 'createdAt' | 'updatedAt'>;
type UpdateInput<T> = <<T, | | >>;
<> {
() {}
(: ): < | > {
....({
: (users., id),
});
}
(: <>): <[]> {
{ filter, orderBy, orderDir = , limit, offset } = options;
....({
: filter ? .(filter) : ,
: orderBy ? (orderDir === ? asc : desc)(users[orderBy]) : ,
limit,
offset,
});
}
}
Service with Business Logic
class UserService {
constructor(
private userRepo: Repository<User>,
private cache: Cache,
private eventBus: EventBus
) {}
async getUser(id: string): Promise<User> {
const cached = await this.cache.get<User>(`user:${id}`);
if (cached) return cached;
const user = await this.userRepo.findById(id);
if (!user) throw new NotFoundError('User', id);
await this.cache.set(`user:${id}`, user, { ttl: 3600 });
return user;
}
async (: ): <> {
existing = ..({
: { : input. },
: ,
});
(existing. > ) {
(, { : });
}
hashedPassword = (input.);
user = ..({
...input,
: hashedPassword,
});
..(, { : user. });
user;
}
(: , : ): <> {
user = ..(id, input);
..();
user;
}
}
Authentication Patterns
JWT with Refresh Tokens
interface TokenPair {
accessToken: string;
refreshToken: string;
}
interface TokenPayload {
sub: string;
email: string;
roles: string[];
type: 'access' | 'refresh';
}
class AuthService {
constructor(
private userRepo: Repository<User>,
private tokenRepo: Repository<RefreshToken>,
private jwtSecret: string
) {}
async login(email: string, password: string): Promise<TokenPair> {
const user = await this.userRepo.findMany({
filter: { email },
limit: 1,
});
if (!user[0] || ! (password, user[].)) {
();
}
.(user[]);
}
(: ): <> {
payload = .(refreshToken);
(payload. !== ) {
();
}
stored = ..(refreshToken);
(!stored || stored.) {
();
}
user = ..(payload.);
(!user) ();
..(refreshToken, { : });
.(user);
}
(: ): {
accessToken = jwt.(
{ : user., : user., : user., : },
.,
{ : }
);
refreshToken = jwt.(
{ : user., : },
.,
{ : }
);
{ accessToken, refreshToken };
}
(: ): {
{
jwt.(token, .) ;
} {
();
}
}
}
Middleware Pattern
type Middleware = (req: Request, next: () => Promise<Response>) => Promise<Response>;
function authMiddleware(requiredRoles?: string[]): Middleware {
return async (req, next) => {
const token = req.headers.get('Authorization')?.replace('Bearer ', '');
if (!token) {
return Response.json(
error('UNAUTHORIZED', 'No token provided'),
{ status: 401 }
);
}
try {
const payload = verifyToken(token);
if (requiredRoles?.length && !requiredRoles.some(r => payload.roles.includes(r))) {
return Response.json(
error('FORBIDDEN', ),
{ : }
);
}
(req ). = payload;
();
} {
.(
(, ),
{ : }
);
}
};
}
(): {
requests = <, { : ; : }>();
(req, next) => {
ip = req..() || ;
now = .();
record = requests.(ip);
(!record || record. < now) {
requests.(ip, { : , : now + windowMs });
();
}
(record. >= limit) {
.(
(, ),
{ : }
);
}
record.++;
();
};
}
Database Patterns
Query Optimization
async function getUsersWithOrders(): Promise<UserWithOrders[]> {
const users = await db.query.users.findMany();
for (const user of users) {
user.orders = await db.query.orders.findMany({
where: eq(orders.userId, user.id),
});
}
return db.query.users.findMany({
with: {
orders: true,
},
});
}
async function paginateUsers(cursor?: string, limit = 20): Promise<{
users: User[];
nextCursor: string | null;
}> {
const users = await db.query.users.findMany({
: cursor ? (users., cursor) : ,
: (users.),
: limit + ,
});
hasMore = users. > limit;
data = hasMore ? users.(, -) : users;
{
: data,
: hasMore ? data[data. - ]. : ,
};
}
Transaction Pattern
async function transferFunds(
fromId: string,
toId: string,
amount: number
): Promise<void> {
await db.transaction(async (tx) => {
const from = await tx.query.accounts.findFirst({
where: eq(accounts.id, fromId),
for: 'update',
});
if (!from || from.balance < amount) {
throw new ValidationError('Insufficient funds', {});
}
await tx.update(accounts)
.set({ balance: from.balance - amount })
.where(eq(accounts.id, fromId));
await tx.update(accounts)
.set({ balance: sql` + ` })
.((accounts., toId));
tx.(transactions).({
fromId,
toId,
amount,
: ,
});
});
}
Caching Patterns
Cache-Aside Pattern
class CachedUserService {
constructor(
private userRepo: Repository<User>,
private cache: Cache
) {}
async getUser(id: string): Promise<User | null> {
const cacheKey = `user:${id}`;
const cached = await this.cache.get<User>(cacheKey);
if (cached) return cached;
const user = await this.userRepo.findById(id);
if (user) {
await this.cache.set(cacheKey, user, { ttl: 3600 });
} else {
await this.cache.set(cacheKey, null, { ttl: 60 });
}
user;
}
(: , : ): <> {
user = ..(id, data);
..();
user;
}
}
Request Deduplication
class RequestDeduplicator {
private pending = new Map<string, Promise<unknown>>();
async dedupe<T>(key: string, fetcher: () => Promise<T>): Promise<T> {
const existing = this.pending.get(key);
if (existing) return existing as Promise<T>;
const promise = fetcher().finally(() => {
this.pending.delete(key);
});
this.pending.set(key, promise);
return promise;
}
}
const deduplicator = new RequestDeduplicator();
async function getUser(id: string): Promise<User> {
return deduplicator.dedupe(`user:`, userRepo.(id));
}
Best Practices Checklist