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 };