Code Templates
Ready-to-use code templates for common patterns in GISE methodology. These templates provide a solid foundation for AI-assisted development while ensuring consistency and best practices.
Service Layer Templates
User Service Template
Use When: Implementing user management and authentication services
import { injectable, inject } from 'inversify';
import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';
import {
UserRepository,
User,
CreateUserDto,
UpdateUserDto,
LoginDto,
AuthResult
} from '../types';
import {
ValidationError,
NotFoundError,
UnauthorizedError,
ConflictError
} from '../errors';
import { Logger } from '../utils/Logger';
import { EmailService } from './EmailService';
@injectable()
export class UserService {
private readonly saltRounds = 12;
private readonly jwtSecret = process.env.JWT_SECRET!;
private readonly jwtExpiresIn = process.env.JWT_EXPIRES_IN || '7d';
constructor(
@inject('UserRepository') private userRepository: UserRepository,
@inject('EmailService') private emailService: EmailService,
@inject('Logger') private logger: Logger
) {}
/**
* Creates a new user account with hashed password
*/
async createUser(userData: CreateUserDto): Promise<User> {
this.logger.info('Creating new user', { email: userData.email });
try {
// Validate user data
await this.validateUserData(userData);
// Check if email already exists
const existingUser = await this.userRepository.findByEmail(userData.email);
if (existingUser) {
throw new ConflictError('Email already registered');
}
// Hash password
const hashedPassword = await bcrypt.hash(userData.password, this.saltRounds);
// Create user
const user = await this.userRepository.create({
...userData,
password: hashedPassword,
status: 'active',
email_verified: false
});
// Send welcome email
await this.emailService.sendWelcomeEmail(user);
this.logger.info('User created successfully', { userId: user.id });
return this.sanitizeUser(user);
} catch (error) {
this.logger.error('Failed to create user', {
email: userData.email,
error: error.message
});
throw error;
}
}
/**
* Authenticates user and returns JWT token
*/
async authenticateUser(loginData: LoginDto): Promise<AuthResult> {
this.logger.info('User authentication attempt', { email: loginData.email });
try {
// Find user by email
const user = await this.userRepository.findByEmail(loginData.email);
if (!user) {
throw new UnauthorizedError('Invalid credentials');
}
// Check if user is active
if (user.status !== 'active') {
throw new UnauthorizedError('Account is not active');
}
// Verify password
const isPasswordValid = await bcrypt.compare(loginData.password, user.password);
if (!isPasswordValid) {
throw new UnauthorizedError('Invalid credentials');
}
// Generate JWT token
const token = jwt.sign(
{
userId: user.id,
email: user.email
},
this.jwtSecret,
{ expiresIn: this.jwtExpiresIn }
);
// Update last login
await this.userRepository.update(user.id, {
last_login: new Date()
});
this.logger.info('User authenticated successfully', { userId: user.id });
return {
token,
user: this.sanitizeUser(user),
expiresIn: this.jwtExpiresIn
};
} catch (error) {
this.logger.error('Authentication failed', {
email: loginData.email,
error: error.message
});
throw error;
}
}
/**
* Updates user information
*/
async updateUser(userId: string, updateData: UpdateUserDto): Promise<User> {
this.logger.info('Updating user', { userId });
try {
const user = await this.userRepository.findById(userId);
if (!user) {
throw new NotFoundError('User not found');
}
// Validate update data
this.validateUpdateData(updateData);
// Hash password if provided
if (updateData.password) {
updateData.password = await bcrypt.hash(updateData.password, this.saltRounds);
}
const updatedUser = await this.userRepository.update(userId, updateData);
this.logger.info('User updated successfully', { userId });
return this.sanitizeUser(updatedUser);
} catch (error) {
this.logger.error('Failed to update user', {
userId,
error: error.message
});
throw error;
}
}
/**
* Validates JWT token and returns user
*/
async validateToken(token: string): Promise<User> {
try {
const decoded = jwt.verify(token, this.jwtSecret) as any;
const user = await this.userRepository.findById(decoded.userId);
if (!user || user.status !== 'active') {
throw new UnauthorizedError('Invalid token');
}
return this.sanitizeUser(user);
} catch (error) {
throw new UnauthorizedError('Invalid token');
}
}
private async validateUserData(userData: CreateUserDto): Promise<void> {
if (!userData.email || !this.isValidEmail(userData.email)) {
throw new ValidationError('Invalid email address', 'email');
}
if (!userData.password || userData.password.length < 8) {
throw new ValidationError('Password must be at least 8 characters', 'password');
}
if (!userData.name || userData.name.trim().length < 2) {
throw new ValidationError('Name must be at least 2 characters', 'name');
}
}
private validateUpdateData(updateData: UpdateUserDto): void {
if (updateData.email && !this.isValidEmail(updateData.email)) {
throw new ValidationError('Invalid email address', 'email');
}
if (updateData.password && updateData.password.length < 8) {
throw new ValidationError('Password must be at least 8 characters', 'password');
}
if (updateData.name && updateData.name.trim().length < 2) {
throw new ValidationError('Name must be at least 2 characters', 'name');
}
}
private isValidEmail(email: string): boolean {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
private sanitizeUser(user: User): User {
const { password, ...sanitized } = user;
return sanitized as User;
}
}
Repository Pattern Templates
Generic Repository Template
Use When: Implementing data access layer with consistent patterns
import { Pool, PoolClient } from 'pg';
import { injectable, inject } from 'inversify';
import { Logger } from '../utils/Logger';
export interface Repository<T, CreateDto, UpdateDto> {
findById(id: string): Promise<T | null>;
findMany(filters?: Record<string, any>): Promise<T[]>;
create(data: CreateDto): Promise<T>;
update(id: string, data: UpdateDto): Promise<T>;
delete(id: string): Promise<void>;
}
@injectable()
export abstract class BaseRepository<T, CreateDto, UpdateDto>
implements Repository<T, CreateDto, UpdateDto> {
protected abstract tableName: string;
protected abstract columns: string[];
constructor(
@inject('Database') protected db: Pool,
@inject('Logger') protected logger: Logger
) {}
async findById(id: string): Promise<T | null> {
const query = `SELECT ${this.columns.join(', ')} FROM ${this.tableName} WHERE id = $1`;
try {
const result = await this.db.query(query, [id]);
return result.rows[0] || null;
} catch (error) {
this.logger.error(`Failed to find ${this.tableName} by ID`, {
id,
error: error.message
});
throw error;
}
}
async findMany(filters: Record<string, any> = {}): Promise<T[]> {
let query = `SELECT ${this.columns.join(', ')} FROM ${this.tableName}`;
const values: any[] = [];
if (Object.keys(filters).length > 0) {
const conditions = Object.keys(filters).map((key, index) => {
values.push(filters[key]);
return `${key} = $${index + 1}`;
});
query += ` WHERE ${conditions.join(' AND ')}`;
}
query += ' ORDER BY created_at DESC';
try {
const result = await this.db.query(query, values);
return result.rows;
} catch (error) {
this.logger.error(`Failed to find ${this.tableName} records`, {
filters,
error: error.message
});
throw error;
}
}
async create(data: CreateDto): Promise<T> {
const fields = Object.keys(data as any);
const placeholders = fields.map((_, index) => `$${index + 1}`);
const values = Object.values(data as any);
const query = `
INSERT INTO ${this.tableName}
(${fields.join(', ')}, created_at, updated_at)
VALUES (${placeholders.join(', ')}, NOW(), NOW())
RETURNING ${this.columns.join(', ')}
`;
try {
const result = await this.db.query(query, values);
this.logger.info(`Created ${this.tableName} record`, { id: result.rows[0].id });
return result.rows[0];
} catch (error) {
this.logger.error(`Failed to create ${this.tableName} record`, {
data,
error: error.message
});
throw error;
}
}
async update(id: string, data: UpdateDto): Promise<T> {
const fields = Object.keys(data as any).filter(key => (data as any)[key] !== undefined);
const setClause = fields.map((field, index) => `${field} = $${index + 2}`);
const values = [id, ...fields.map(field => (data as any)[field])];
if (fields.length === 0) {
throw new Error('No fields to update');
}
const query = `
UPDATE ${this.tableName}
SET ${setClause.join(', ')}, updated_at = NOW()
WHERE id = $1
RETURNING ${this.columns.join(', ')}
`;
try {
const result = await this.db.query(query, values);
if (result.rows.length === 0) {
throw new Error(`${this.tableName} record not found`);
}
this.logger.info(`Updated ${this.tableName} record`, { id });
return result.rows[0];
} catch (error) {
this.logger.error(`Failed to update ${this.tableName} record`, {
id,
data,
error: error.message
});
throw error;
}
}
async delete(id: string): Promise<void> {
const query = `DELETE FROM ${this.tableName} WHERE id = $1`;
try {
const result = await this.db.query(query, [id]);
if (result.rowCount === 0) {
throw new Error(`${this.tableName} record not found`);
}
this.logger.info(`Deleted ${this.tableName} record`, { id });
} catch (error) {
this.logger.error(`Failed to delete ${this.tableName} record`, {
id,
error: error.message
});
throw error;
}
}
protected async executeInTransaction<R>(
operation: (client: PoolClient) => Promise<R>
): Promise<R> {
const client = await this.db.connect();
try {
await client.query('BEGIN');
const result = await operation(client);
await client.query('COMMIT');
return result;
} catch (error) {
await client.query('ROLLBACK');
throw error;
} finally {
client.release();
}
}
}
// Concrete implementation example
@injectable()
export class UserRepository extends BaseRepository<User, CreateUserDto, UpdateUserDto> {
protected tableName = 'users';
protected columns = ['id', 'email', 'name', 'status', 'created_at', 'updated_at'];
async findByEmail(email: string): Promise<User | null> {
const query = `SELECT ${this.columns.join(', ')} FROM ${this.tableName} WHERE email = $1`;
try {
const result = await this.db.query(query, [email]);
return result.rows[0] || null;
} catch (error) {
this.logger.error('Failed to find user by email', {
email,
error: error.message
});
throw error;
}
}
async findActiveUsers(): Promise<User[]> {
return this.findMany({ status: 'active' });
}
}
Controller Templates
REST Controller Template
Use When: Creating RESTful API endpoints with proper error handling
import { Request, Response, NextFunction } from 'express';
import { injectable, inject } from 'inversify';
import { UserService } from '../services/UserService';
import {
CreateUserDto,
UpdateUserDto,
LoginDto
} from '../types';
import { Logger } from '../utils/Logger';
import { validateRequest } from '../middleware/validation';
import { authenticate } from '../middleware/auth';
import {
createUserSchema,
updateUserSchema,
loginSchema
} from '../schemas/userSchemas';
@injectable()
export class UserController {
constructor(
@inject('UserService') private userService: UserService,
@inject('Logger') private logger: Logger
) {}
/**
* POST /api/users - Create new user
*/
createUser = [
validateRequest(createUserSchema),
async (req: Request, res: Response, next: NextFunction) => {
try {
const userData: CreateUserDto = req.body;
const user = await this.userService.createUser(userData);
res.status(201).json({
success: true,
data: user,
message: 'User created successfully'
});
} catch (error) {
next(error);
}
}
];
/**
* POST /api/auth/login - Authenticate user
*/
login = [
validateRequest(loginSchema),
async (req: Request, res: Response, next: NextFunction) => {
try {
const loginData: LoginDto = req.body;
const authResult = await this.userService.authenticateUser(loginData);
res.json({
success: true,
data: authResult,
message: 'Authentication successful'
});
} catch (error) {
next(error);
}
}
];
/**
* GET /api/users/:id - Get user by ID
*/
getUserById = [
authenticate,
async (req: Request, res: Response, next: NextFunction) => {
try {
const { id } = req.params;
const user = await this.userService.getUserById(id);
res.json({
success: true,
data: user
});
} catch (error) {
next(error);
}
}
];
/**
* PUT /api/users/:id - Update user
*/
updateUser = [
authenticate,
validateRequest(updateUserSchema),
async (req: Request, res: Response, next: NextFunction) => {
try {
const { id } = req.params;
const updateData: UpdateUserDto = req.body;
// Authorization check
if (req.user!.id !== id && req.user!.role !== 'admin') {
return res.status(403).json({
success: false,
error: 'Insufficient permissions'
});
}
const user = await this.userService.updateUser(id, updateData);
res.json({
success: true,
data: user,
message: 'User updated successfully'
});
} catch (error) {
next(error);
}
}
];
/**
* GET /api/users - List users (admin only)
*/
listUsers = [
authenticate,
async (req: Request, res: Response, next: NextFunction) => {
try {
// Authorization check
if (req.user!.role !== 'admin') {
return res.status(403).json({
success: false,
error: 'Admin access required'
});
}
const { page = 1, limit = 20, search } = req.query;
const users = await this.userService.listUsers({
page: Number(page),
limit: Number(limit),
search: search as string
});
res.json({
success: true,
data: users
});
} catch (error) {
next(error);
}
}
];
/**
* DELETE /api/users/:id - Delete user
*/
deleteUser = [
authenticate,
async (req: Request, res: Response, next: NextFunction) => {
try {
const { id } = req.params;
// Authorization check
if (req.user!.role !== 'admin') {
return res.status(403).json({
success: false,
error: 'Admin access required'
});
}
await this.userService.deleteUser(id);
res.status(204).send();
} catch (error) {
next(error);
}
}
];
}
Middleware Templates
Authentication Middleware
Use When: Implementing JWT-based authentication
import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';
import { injectable, inject } from 'inversify';
import { UserService } from '../services/UserService';
import { Logger } from '../utils/Logger';
import { UnauthorizedError } from '../errors';
// Extend Express Request type
declare global {
namespace Express {
interface Request {
user?: User;
}
}
}
@injectable()
export class AuthMiddleware {
constructor(
@inject('UserService') private userService: UserService,
@inject('Logger') private logger: Logger
) {}
/**
* JWT Authentication middleware
*/
authenticate = async (req: Request, res: Response, next: NextFunction) => {
try {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
throw new UnauthorizedError('Authorization header required');
}
const token = authHeader.substring(7); // Remove 'Bearer ' prefix
const user = await this.userService.validateToken(token);
req.user = user;
next();
} catch (error) {
this.logger.warn('Authentication failed', {
path: req.path,
ip: req.ip,
error: error.message
});
res.status(401).json({
success: false,
error: 'Authentication required'
});
}
};
/**
* Role-based authorization middleware
*/
authorize = (requiredRoles: string[]) => {
return (req: Request, res: Response, next: NextFunction) => {
if (!req.user) {
return res.status(401).json({
success: false,
error: 'Authentication required'
});
}
if (!requiredRoles.includes(req.user.role)) {
this.logger.warn('Authorization failed', {
userId: req.user.id,
requiredRoles,
userRole: req.user.role,
path: req.path
});
return res.status(403).json({
success: false,
error: 'Insufficient permissions'
});
}
next();
};
};
/**
* Optional authentication - sets user if token is valid but doesn't require it
*/
optionalAuth = async (req: Request, res: Response, next: NextFunction) => {
try {
const authHeader = req.headers.authorization;
if (authHeader && authHeader.startsWith('Bearer ')) {
const token = authHeader.substring(7);
const user = await this.userService.validateToken(token);
req.user = user;
}
} catch (error) {
// Log but don't fail for optional auth
this.logger.debug('Optional auth failed', { error: error.message });
}
next();
};
}
API Documentation Templates
Swagger/OpenAPI Integration
Use When: Generating API documentation from code
import swaggerJsdoc from 'swagger-jsdoc';
import swaggerUi from 'swagger-ui-express';
import { Express } from 'express';
const options = {
definition: {
openapi: '3.0.0',
info: {
title: 'API Documentation',
version: '1.0.0',
description: 'Comprehensive API documentation with examples',
},
servers: [
{
url: process.env.API_BASE_URL || 'http://localhost:3000',
description: 'Development server',
},
],
components: {
securitySchemes: {
BearerAuth: {
type: 'http',
scheme: 'bearer',
bearerFormat: 'JWT',
},
},
},
security: [
{
BearerAuth: [],
},
],
},
apis: ['./src/routes/*.ts', './src/controllers/*.ts'], // paths to files containing OpenAPI definitions
};
const specs = swaggerJsdoc(options);
export function setupSwagger(app: Express): void {
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(specs, {
explorer: true,
customCss: '.swagger-ui .topbar { display: none }',
customSiteTitle: "API Documentation"
}));
}
/**
* @swagger
* components:
* schemas:
* User:
* type: object
* required:
* - id
* - email
* - name
* properties:
* id:
* type: string
* format: uuid
* description: User's unique identifier
* email:
* type: string
* format: email
* description: User's email address
* name:
* type: string
* description: User's full name
* created_at:
* type: string
* format: date-time
* description: Account creation timestamp
* example:
* id: d290f1ee-6c54-4b01-90e6-d701748f0851
* email: john.doe@example.com
* name: John Doe
* created_at: 2024-01-15T09:30:00Z
*
* CreateUserRequest:
* type: object
* required:
* - email
* - name
* - password
* properties:
* email:
* type: string
* format: email
* name:
* type: string
* minLength: 2
* maxLength: 100
* password:
* type: string
* minLength: 8
* example:
* email: john.doe@example.com
* name: John Doe
* password: securepassword123
*/
/**
* @swagger
* /api/users:
* post:
* summary: Create a new user
* tags: [Users]
* security: []
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/CreateUserRequest'
* responses:
* 201:
* description: User created successfully
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* data:
* $ref: '#/components/schemas/User'
* message:
* type: string
* 400:
* description: Validation error
* 409:
* description: Email already exists
*/
Error Handling Templates
Custom Error Classes
Use When: Implementing consistent error handling across the application
// Base error class
export abstract class AppError extends Error {
abstract readonly statusCode: number;
abstract readonly isOperational: boolean;
public readonly timestamp: string;
constructor(
message: string,
public readonly context?: Record<string, any>
) {
super(message);
this.name = this.constructor.name;
this.timestamp = new Date().toISOString();
Error.captureStackTrace(this, this.constructor);
}
}
// Specific error implementations
export class ValidationError extends AppError {
readonly statusCode = 400;
readonly isOperational = true;
constructor(message: string, public readonly field?: string) {
super(message, { field });
}
}
export class NotFoundError extends AppError {
readonly statusCode = 404;
readonly isOperational = true;
}
export class UnauthorizedError extends AppError {
readonly statusCode = 401;
readonly isOperational = true;
}
export class ForbiddenError extends AppError {
readonly statusCode = 403;
readonly isOperational = true;
}
export class ConflictError extends AppError {
readonly statusCode = 409;
readonly isOperational = true;
}
export class RateLimitError extends AppError {
readonly statusCode = 429;
readonly isOperational = true;
}
// Error handler middleware
export const errorHandler = (
error: Error,
req: Request,
res: Response,
next: NextFunction
): void => {
const logger = Container.get<Logger>('Logger');
logger.error('Request failed', {
error: error.message,
stack: error.stack,
path: req.path,
method: req.method,
body: req.body,
params: req.params,
query: req.query,
userId: req.user?.id,
timestamp: new Date().toISOString()
});
if (error instanceof AppError) {
res.status(error.statusCode).json({
success: false,
error: error.message,
...(error.context && { details: error.context }),
...(process.env.NODE_ENV === 'development' && { stack: error.stack })
});
return;
}
// Handle specific known errors
if (error.name === 'ValidationError') {
res.status(400).json({
success: false,
error: 'Validation failed',
details: error.message
});
return;
}
if (error.name === 'JsonWebTokenError') {
res.status(401).json({
success: false,
error: 'Invalid token'
});
return;
}
// Unhandled errors
res.status(500).json({
success: false,
error: 'Internal server error',
...(process.env.NODE_ENV === 'development' && {
message: error.message,
stack: error.stack
})
});
};
Related Resources
Next: Quality Templates | Configuration Files
Well-structured code templates accelerate development while ensuring consistency and best practices.