npm package: @200systems/mf-express-adapter
Description: Express.js integration adapter with middleware, controllers, and error handling
Author: Cleiton Marques
Latest version: 1.2.6

# @200systems/mf-express-adapter

Express.js integration adapter para o microframework TypeScript com middleware estruturado, controllers base e tratamento de erros.

## Features Implementadas

- **🚀 ExpressApp Class** - Classe principal com configuração Express pré-configurada
- **📋 BaseController** - Controller base com async handlers e utilitários
- **🛡️ Middleware Stack** - Error handling, request logging e response formatting
- **🔐 JWT Authentication** - Middleware de autenticação JWT com role-based access
- **📊 Response Helpers** - Formatação padronizada de respostas da API  
- **✅ Validation Utilities** - Helpers de validação e sanitização
- **📄 Pagination Support** - Utilitários de paginação e ordenação

## Instalação

```bash
npm install @200systems/mf-express-adapter
```

### Peer Dependencies

```bash
npm install express cors helmet compression express-rate-limit jsonwebtoken
npm install --save-dev @types/express @types/cors @types/compression @types/jsonwebtoken
```

## Quick Start

### 1. Configuração Básica da Aplicação

```typescript
// src/server.ts
import { ExpressApp } from '@200systems/mf-express-adapter';
import { createLogger } from '@200systems/mf-logger';

const app = new ExpressApp({
  port: 3000,
  host: '0.0.0.0',
  cors: {
    origin: process.env.CORS_ORIGIN || 'http://localhost:5173',
    credentials: true
  },
  trustProxy: true,
  security: {
    helmet: true,
    rateLimit: {
      windowMs: 15 * 60 * 1000,
      max: 100
    }
  }
});

app.start().then(() => {
  console.log('Server started successfully');
});
```

### 2. JWT Authentication Setup

```typescript
// src/middleware/auth.ts
import { JwtMiddleware } from '@200systems/mf-express-adapter';
import { createLogger } from '@200systems/mf-logger';

const logger = createLogger({ context: 'auth' });

export const jwtAuth = new JwtMiddleware({
  secret: process.env.JWT_SECRET || 'your-secret-key',
  algorithms: ['HS256'],
  skipPaths: ['/auth/login', '/auth/register', '/health'],
  headerName: 'authorization',
  cookieName: 'token'
}, logger);

// Usage in routes
import { Router } from 'express';
const router = Router();

// Protected route
router.get('/profile', jwtAuth.authenticate, userController.getProfile);

// Optional auth (user context if token present)
router.get('/public', jwtAuth.optional, contentController.getPublicContent);

// Role-based access
router.delete('/admin/users/:id', 
  jwtAuth.authenticate, 
  jwtAuth.requireRole(['admin', 'super-admin']), 
  adminController.deleteUser
);
```

### 3. Controller Implementation

```typescript
// src/controllers/UserController.ts
import { Request, Response, NextFunction } from 'express';
import { BaseController, AuthenticatedRequest } from '@200systems/mf-express-adapter';
import { Logger } from '@200systems/mf-logger';
import { ServiceResult } from '@200systems/mf-service-base';

export class UserController extends BaseController {
  constructor(logger: Logger, private userService: UserService) {
    super(logger);
  }

  getProfile = this.asyncHandler(async (req: AuthenticatedRequest, res: Response): Promise<ServiceResult<User>> => {
    const userId = req.user!.userId; // Safe because of JWT middleware
    const user = await this.userService.getUserById(userId);
    return ServiceResult.success(user);
  });

  login = this.asyncHandler(async (req: Request, res: Response): Promise<ServiceResult<LoginResponse>> => {
    const { email, password } = req.body;
    
    const user = await this.userService.authenticateUser(email, password);
    const token = JwtMiddleware.generateToken(
      { userId: user.id, email: user.email, roles: user.roles },
      process.env.JWT_SECRET!,
      '24h'
    );

    return ServiceResult.success({ user, token });
  });
}
```

### 4. Login/Logout Implementation

```typescript
// src/controllers/AuthController.ts
export class AuthController extends BaseController {
  login = this.asyncHandler(async (req: Request, res: Response): Promise<void> => {
    const { email, password } = req.body;
    
    const user = await this.authService.login(email, password);
    const token = JwtMiddleware.generateToken(
      { 
        userId: user.id, 
        email: user.email, 
        roles: user.roles || []
      },
      process.env.JWT_SECRET!,
      '24h'
    );

    // Set as cookie (optional)
    res.cookie('token', token, {
      httpOnly: true,
      secure: process.env.NODE_ENV === 'production',
      maxAge: 24 * 60 * 60 * 1000 // 24 hours
    });

    res.json({
      success: true,
      data: { user, token },
      meta: { timestamp: new Date().toISOString() }
    });
  });

  logout = this.asyncHandler(async (req: Request, res: Response): Promise<void> => {
    res.clearCookie('token');
    res.json({
      success: true,
      message: 'Logged out successfully',
      meta: { timestamp: new Date().toISOString() }
    });
  });
}
```

## API Reference

### JwtMiddleware

```typescript
interface JwtMiddlewareConfig {
  secret?: string;
  algorithms?: string[];
  skipPaths?: string[];
  cookieName?: string;
  headerName?: string;
  onTokenExpired?: (req: Request, res: Response) => void;
  onTokenInvalid?: (req: Request, res: Response) => void;
}

class JwtMiddleware {
  constructor(config: JwtMiddlewareConfig, logger: Logger);
  
  // Required authentication
  authenticate: RequestHandler;
  
  // Optional authentication
  optional: RequestHandler;
  
  // Role-based access control
  requireRole(roles: string | string[]): RequestHandler;
  
  // Static helpers
  static generateToken(payload: JwtPayload, secret: string, expiresIn?: string): string;
  static decodeToken(token: string): JwtPayload | null;
}
```

### JWT Payload Structure

```typescript
interface JwtPayload {
  userId: number;
  email: string;
  roles?: string[];
  [key: string]: unknown;
}
```

### Authentication Patterns

```typescript
// 1. Basic protected route
router.get('/protected', jwtAuth.authenticate, controller.method);

// 2. Optional authentication
router.get('/mixed', jwtAuth.optional, (req: AuthenticatedRequest, res) => {
  if (req.user) {
    // User is authenticated
  } else {
    // Anonymous access
  }
});

// 3. Role-based access
router.post('/admin', 
  jwtAuth.authenticate,
  jwtAuth.requireRole('admin'),
  adminController.method
);

// 4. Multiple roles
router.delete('/resource/:id',
  jwtAuth.authenticate,
  jwtAuth.requireRole(['admin', 'moderator']),
  controller.method
);
```

### ExpressApp

```typescript
interface ExpressConfig {
  port?: number;
  host?: string;
  cors?: {
    origin?: string | string[] | boolean;
    credentials?: boolean;
    methods?: string[];
    allowedHeaders?: string[];
  };
  security?: {
    helmet?: boolean;
    rateLimit?: {
      windowMs?: number;
      max?: number;
      message?: string;
    };
  };
  compression?: boolean;
  bodyParser?: {
    json?: { limit?: string };
    urlencoded?: { limit?: string; extended?: boolean };
  };
  logger?: Partial<LoggerConfig>;
  trustProxy?: boolean;
}
```

### BaseController

```typescript
abstract class BaseController {
  constructor(logger: Logger);
  
  protected asyncHandler(handler: RouteHandler): RouteHandler;
  protected getIdFromParams(req: Request, paramName?: string): string;
  protected getQueryParam(req: Request, paramName: string, defaultValue?: string): string | undefined;
  protected getQueryParamAsNumber(req: Request, paramName: string, defaultValue?: number): number | undefined;
  protected getPaginationParams(req: Request): { page: number; limit: number; offset: number };
}
```

### Response Helpers

```typescript
// Success responses
ResponseHelper.success(res, data, 'Operation successful');
ResponseHelper.created(res, newUser, 'User created');
ResponseHelper.noContent(res);

// Error responses  
ResponseHelper.error(res, 'Something went wrong', 400);
ResponseHelper.notFound(res, 'User not found');
ResponseHelper.unauthorized(res, 'Invalid credentials');
ResponseHelper.forbidden(res, 'Access denied');

// Authentication responses
ResponseHelper.unauthorized(res); // 401
ResponseHelper.forbidden(res);    // 403
```

## Authentication Error Responses

JWT middleware returns standardized error responses:

```typescript
// No token provided
{
  "success": false,
  "error": {
    "message": "No token provided",
    "code": "NO_TOKEN"
  },
  "meta": {
    "timestamp": "2024-01-15T10:30:00.000Z",
    "requestId": "abc123"
  }
}

// Token expired
{
  "success": false,
  "error": {
    "message": "Token has expired",
    "code": "TOKEN_EXPIRED"
  }
}

// Invalid token
{
  "success": false,
  "error": {
    "message": "Invalid token", 
    "code": "TOKEN_INVALID"
  }
}

// Insufficient permissions
{
  "success": false,
  "error": {
    "message": "Insufficient permissions",
    "code": "INSUFFICIENT_PERMISSIONS",
    "details": {
      "requiredRoles": ["admin"],
      "userRoles": ["user"]
    }
  }
}
```

## Middleware Stack Automático

A classe `ExpressApp` configura automaticamente:

- **Security**: Helmet para headers de segurança
- **Rate Limiting**: Proteção contra ataques de força bruta
- **CORS**: Configuração de Cross-Origin Resource Sharing
- **Compression**: Compressão gzip/deflate
- **Body Parsing**: JSON e URL-encoded parsing
- **Request Logging**: Log automático de requests com timing
- **Response Formatting**: Formatação padronizada de respostas
- **Error Handling**: Tratamento global de erros

## Exemplo Completo com Autenticação

```typescript
// src/server.ts
import { ExpressApp, JwtMiddleware } from '@200systems/mf-express-adapter';
import { createLogger } from '@200systems/mf-logger';
import { UserController } from './controllers/UserController';
import { AuthController } from './controllers/AuthController';

const logger = createLogger({ context: 'api-server' });

// JWT Setup
const jwtAuth = new JwtMiddleware({
  secret: process.env.JWT_SECRET!,
  skipPaths: ['/auth/login', '/auth/register', '/health'],
  algorithms: ['HS256']
}, logger);

// Express App
const app = new ExpressApp({
  port: 3000,
  cors: { origin: true, credentials: true },
  trustProxy: true
});

// Auth routes (no JWT required)
app.addRoutes([
  { method: 'post', path: '/auth/login', handler: authController.login },
  { method: 'post', path: '/auth/register', handler: authController.register },
  { method: 'post', path: '/auth/logout', handler: authController.logout }
]);

// Protected routes
app.addRoutes([
  { 
    method: 'get', 
    path: '/api/profile', 
    handler: userController.getProfile,
    middleware: [jwtAuth.authenticate]
  },
  { 
    method: 'get', 
    path: '/api/admin/users', 
    handler: userController.getAllUsers,
    middleware: [jwtAuth.authenticate, jwtAuth.requireRole('admin')]
  }
]);

app.start();
```

## Integração com Outros Packages

Este adapter funciona perfeitamente com:

- `@200systems/mf-logger` - Sistema de logging (obrigatório)
- `@200systems/mf-service-base` - Base classes para services (obrigatório)
- `@200systems/mf-db-core` - Abstração de banco de dados

## Dependencies

### Required
- `@200systems/mf-logger` - Logging functionality
- `@200systems/mf-service-base` - Service base classes
- `@200systems/mf-db-core` - Database core abstractions

### Peer Dependencies
- `express` - Express.js framework
- `cors` - CORS middleware
- `helmet` - Security middleware
- `compression` - Response compression
- `express-rate-limit` - Rate limiting
- `jsonwebtoken` - JWT token handling

## License

MIT