import express, { Express, Request, Response, NextFunction } from 'express';
import cors from 'cors';
// ============================================
// Types
// ============================================
interface User {
id: number;
email: string;
name: string;
createdAt: Date;
}
interface CreateUserDTO {
email: string;
name: string;
password: string;
}
interface ApiResponse<T> {
success: boolean;
data?: T;
error?: string;
}
// ============================================
// Middleware
// ============================================
const errorHandler = (err: Error, req: Request, res: Response, next: NextFunction) => {
console.error(`[ERROR] ${err.message}`, err.stack);
res.status(500).json({
success: false,
error: process.env.NODE_ENV === 'production' ? 'Internal server error' : err.message
});
};
const requestLogger = (req: Request, res: Response, next: NextFunction) => {
const start = Date.now();
res.on('finish', () => {
const duration = Date.now() - start;
console.log(`[${req.method}] ${req.path} - ${res.statusCode} (${duration}ms)`);
});
next();
};
const validateBody = <T>(schema: (body: unknown) => T) => {
return (req: Request, res: Response, next: NextFunction) => {
try {
req.body = schema(req.body);
next();
} catch (error) {
res.status(400).json({ success: false, error: (error as Error).message });
}
};
};
// ============================================
// Simple In-Memory Database
// ============================================
class Database {
private users: Map<number, User> = new Map();
private nextId = 1;
createUser(data: CreateUserDTO): User {
const user: User = {
id: this.nextId++,
email: data.email,
name: data.name,
createdAt: new Date()
};
this.users.set(user.id, user);
return user;
}
getUser(id: number): User | undefined {
return this.users.get(id);
}
getAllUsers(): User[] {
return Array.from(this.users.values());
}
updateUser(id: number, data: Partial<User>): User | undefined {
const user = this.users.get(id);
if (!user) return undefined;
const updated = { ...user, ...data };
this.users.set(id, updated);
return updated;
}
deleteUser(id: number): boolean {
return this.users.delete(id);
}
}
// ============================================
// Controllers
// ============================================
const db = new Database();
const userController = {
getAll: async (req: Request, res: Response) => {
const users = db.getAllUsers();
res.json({ success: true, data: users });
},
getById: async (req: Request, res: Response) => {
const id = parseInt(req.params.id);
const user = db.getUser(id);
if (!user) {
return res.status(404).json({ success: false, error: 'User not found' });
}
res.json({ success: true, data: user });
},
create: async (req: Request, res: Response) => {
const user = db.createUser(req.body);
res.status(201).json({ success: true, data: user });
},
update: async (req: Request, res: Response) => {
const id = parseInt(req.params.id);
const user = db.updateUser(id, req.body);
if (!user) {
return res.status(404).json({ success: false, error: 'User not found' });
}
res.json({ success: true, data: user });
},
delete: async (req: Request, res: Response) => {
const id = parseInt(req.params.id);
const deleted = db.deleteUser(id);
if (!deleted) {
return res.status(404).json({ success: false, error: 'User not found' });
}
res.status(204).send();
}
};
// ============================================
// Router
// ============================================
const userRouter = express.Router();
userRouter.get('/', userController.getAll);
userRouter.get('/:id', userController.getById);
userRouter.post('/', userController.create);
userRouter.put('/:id', userController.update);
userRouter.delete('/:id', userController.delete);
// ============================================
// App Setup
// ============================================
const app: Express = express();
// Middleware
app.use(cors());
app.use(express.json());
app.use(requestLogger);
// Health check
app.get('/health', (req, res) => {
res.json({ status: 'healthy', timestamp: new Date().toISOString() });
});
// Routes
app.use('/api/users', userRouter);
// Error handler (must be last)
app.use(errorHandler);
// Start server
const PORT = process.env.PORT || 3000;
if (require.main === module) {
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});
}
export { app, db };