feat: add Express API base structure

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Consultoria AS
2026-01-22 01:50:22 +00:00
parent 830e98625d
commit af617627a4
7 changed files with 154 additions and 0 deletions

31
apps/api/src/app.ts Normal file
View File

@@ -0,0 +1,31 @@
import express from 'express';
import cors from 'cors';
import helmet from 'helmet';
import { env } from './config/env.js';
import { errorMiddleware } from './middlewares/error.middleware.js';
const app = express();
// Security
app.use(helmet());
app.use(cors({
origin: env.CORS_ORIGIN,
credentials: true,
}));
// Body parsing
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// Health check
app.get('/health', (req, res) => {
res.json({ status: 'ok', timestamp: new Date().toISOString() });
});
// API Routes (to be added)
// app.use('/api/auth', authRoutes);
// Error handling
app.use(errorMiddleware);
export { app };

View File

@@ -0,0 +1,20 @@
import { z } from 'zod';
const envSchema = z.object({
NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
PORT: z.string().default('4000'),
DATABASE_URL: z.string(),
JWT_SECRET: z.string().min(32),
JWT_EXPIRES_IN: z.string().default('15m'),
JWT_REFRESH_EXPIRES_IN: z.string().default('7d'),
CORS_ORIGIN: z.string().default('http://localhost:3000'),
});
const parsed = envSchema.safeParse(process.env);
if (!parsed.success) {
console.error('❌ Invalid environment variables:', parsed.error.flatten().fieldErrors);
process.exit(1);
}
export const env = parsed.data;

9
apps/api/src/index.ts Normal file
View File

@@ -0,0 +1,9 @@
import { app } from './app.js';
import { env } from './config/env.js';
const PORT = parseInt(env.PORT, 10);
app.listen(PORT, () => {
console.log(`🚀 API Server running on http://localhost:${PORT}`);
console.log(`📊 Environment: ${env.NODE_ENV}`);
});

View File

@@ -0,0 +1,33 @@
import type { Request, Response, NextFunction } from 'express';
export class AppError extends Error {
constructor(
public statusCode: number,
public message: string,
public isOperational = true
) {
super(message);
Object.setPrototypeOf(this, AppError.prototype);
}
}
export function errorMiddleware(
err: Error,
req: Request,
res: Response,
next: NextFunction
) {
if (err instanceof AppError) {
return res.status(err.statusCode).json({
status: 'error',
message: err.message,
});
}
console.error('Unhandled error:', err);
return res.status(500).json({
status: 'error',
message: 'Internal server error',
});
}