rivia-koa-middlewares
v1.5.1
Published
Library with koajs middlewares
Downloads
260
Readme
rivia-koa-middlewares
Biblioteca de middlewares para aplicações Koa.js que fornece funcionalidades essenciais como autenticação, validação, logging, rastreamento, idempotência e formatação de respostas JSON.
Instalação
npm install rivia-koa-middlewaresMiddlewares Disponíveis
1. authorizer (Cognito Authorizer)
Middleware para autenticação e autorização usando AWS Cognito JWT tokens.
Funcionamento
- Verifica a presença do header
Authorizationna requisição - Valida o token JWT usando AWS Cognito JWT Verifier
- Extrai informações do usuário do token (sub, cognito:username, cognito:groups)
- Armazena as informações do usuário em
ctx.state.user - Suporta validação de grupos do Cognito
- Permite extrair campos customizados do token
Variáveis de Ambiente Necessárias
COGNITO_USER_POOL_ID=seu-user-pool-id
COGNITO_CLIENT_ID=seu-client-idExemplo de Uso
import Koa from 'koa';
import Router from 'koa-router';
import { authorizer } from 'rivia-koa-middlewares';
const app = new Koa();
const router = new Router();
// Uso básico - apenas valida o token
router.get('/protected', authorizer(), async (ctx) => {
// ctx.state.user contém: { sub, cognito:username, cognito:groups }
ctx.body = { message: 'Acesso autorizado', user: ctx.state.user };
});
// Com validação de grupos
router.get('/admin', authorizer({ groups: ['admin'] }), async (ctx) => {
ctx.body = { message: 'Acesso de administrador' };
});
// Com múltiplos grupos permitidos
router.get('/staff', authorizer({ groups: ['user', 'admin'] }), async (ctx) => {
ctx.body = { message: 'Acesso de staff' };
});
// Com campos customizados do token
router.get('/custom', authorizer({
customFieldNames: ['email', 'custom:role']
}), async (ctx) => {
// ctx.state.user agora inclui também email e custom:role
ctx.body = { user: ctx.state.user };
});
// Com tipo de token diferente (access token ao invés de id token)
router.get('/access', authorizer({ tokenType: 'access' }), async (ctx) => {
ctx.body = { message: 'Usando access token' };
});
app.use(router.routes());
app.listen(3000);Opções de Configuração
groups(array): Lista de grupos do Cognito permitidos. Se especificado, o usuário deve pertencer a pelo menos um dos grupos.tokenType(string): Tipo de token a ser validado. Padrão:'id'. Pode ser'id'ou'access'.customFieldNames(array): Lista de campos adicionais do token JWT a serem extraídos e incluídos emctx.state.user.
Respostas de Erro
401: Quando o headerAuthorizationestá ausente403: Quando o token é inválido ou o usuário não pertence aos grupos requeridos
2. idempotence
Middleware que garante idempotência de requisições, evitando processamento duplicado.
Funcionamento
- Gera um hash único baseado no método HTTP, URL, body da requisição e ID do usuário (se autenticado)
- Armazena a resposta da primeira requisição no DynamoDB
- Para requisições idênticas dentro do período de expiração, retorna a resposta armazenada sem processar novamente
- Usa o DynamoDB para armazenar as respostas com TTL configurável
Exemplo de Uso
import Koa from 'koa';
import Router from 'koa-router';
import bodyParser from 'koa-bodyparser';
import { idempotence } from 'rivia-koa-middlewares';
const app = new Koa();
const router = new Router();
app.use(bodyParser());
// Idempotência com tempo padrão de 30 segundos
router.post('/create-order', idempotence(), async (ctx) => {
// Esta lógica só será executada na primeira requisição
// Requisições idênticas nos próximos 30 segundos retornarão a mesma resposta
const order = await createOrder(ctx.request.body);
ctx.body = { orderId: order.id, status: 'created' };
});
// Idempotência com tempo customizado (10 segundos)
router.post('/payment', idempotence(10), async (ctx) => {
const payment = await processPayment(ctx.request.body);
ctx.body = { paymentId: payment.id, status: 'processed' };
});
app.use(router.routes());
app.listen(3000);Parâmetros
secondsToExpire(number, opcional): Tempo em segundos para expirar o cache da resposta. Padrão:30.
Como Funciona
- Primeira requisição: Processa normalmente e armazena a resposta no DynamoDB
- Requisições subsequentes idênticas: Retorna a resposta armazenada sem processar
- Após expiração: A próxima requisição idêntica será processada novamente
Nota: O hash considera método, URL, body e o sub do usuário (se ctx.state.user.sub existir), garantindo que diferentes usuários tenham caches separados.
3. jsonResponse
Middleware que formata respostas como JSON e adiciona tratamento de erros padronizado.
Funcionamento
- Converte objetos JavaScript em strings JSON
- Adiciona
requestIdao corpo da resposta (se disponível emctx.requestId) - Define o content-type como
application/json - Captura erros e formata respostas de erro de forma consistente
- Preserva strings simples sem conversão
Exemplo de Uso
import Koa from 'koa';
import Router from 'koa-router';
import bodyParser from 'koa-bodyparser';
import { jsonResponse, trace } from 'rivia-koa-middlewares';
const app = new Koa();
const router = new Router();
app.use(bodyParser());
app.use(trace); // Necessário para gerar ctx.requestId
app.use(jsonResponse);
router.get('/success', async (ctx) => {
ctx.body = { message: 'Sucesso', data: { id: 1 } };
// Resposta será: {"message":"Sucesso","data":{"id":1},"requestId":"uuid-gerado"}
});
router.get('/error', async (ctx) => {
throw new Error('Algo deu errado');
// Resposta será: {"message":"Algo deu errado","requestId":"uuid-gerado"}
// Status: 500
});
router.get('/custom-error', async (ctx) => {
const error = new Error('Não encontrado');
error.statusCode = 404;
throw error;
// Resposta será: {"message":"Não encontrado","requestId":"uuid-gerado"}
// Status: 404
});
router.get('/string', async (ctx) => {
ctx.body = 'Resposta simples';
// Resposta será: "Resposta simples" (sem conversão JSON)
});
app.use(router.routes());
app.listen(3000);Comportamento
- Objetos: Convertidos para JSON string e recebem
requestIdse disponível - Strings: Mantidas como estão, sem conversão
- Erros: Capturados automaticamente, status code do erro é preservado (padrão: 500)
4. inOutLogger
Middleware para logging de requisições e respostas HTTP.
Funcionamento
- Registra a requisição completa no início
- Calcula o tempo de processamento
- Registra método, URL, tempo de resposta e status HTTP
- Registra a resposta completa ao final
Exemplo de Uso
import Koa from 'koa';
import Router from 'koa-router';
import { inOutLogger } from 'rivia-koa-middlewares';
const app = new Koa();
const router = new Router();
app.use(inOutLogger);
router.get('/users', async (ctx) => {
ctx.body = { users: [] };
});
router.post('/users', async (ctx) => {
ctx.body = { id: 1, name: 'John' };
});
app.use(router.routes());
app.listen(3000);Logs Gerados
O middleware gera os seguintes logs (usando rivia-logs):
Request- Objeto completo da requisiçãoGET /users - 15ms- Método, URL e tempo de processamentoStatus: 200- Status HTTP da respostaResponse- Objeto completo da resposta
5. trace
Middleware que gera um ID único de rastreamento para cada requisição.
Funcionamento
- Gera um UUID v4 único para cada requisição
- Armazena o ID em
ctx.requestId - Útil para rastreamento e correlação de logs
Exemplo de Uso
import Koa from 'koa';
import Router from 'koa-router';
import { trace, jsonResponse } from 'rivia-koa-middlewares';
const app = new Koa();
const router = new Router();
app.use(trace);
app.use(jsonResponse); // jsonResponse usa ctx.requestId
router.get('/users', async (ctx) => {
// ctx.requestId contém um UUID único
console.log('Request ID:', ctx.requestId);
ctx.body = { users: [], requestId: ctx.requestId };
});
app.use(router.routes());
app.listen(3000);Integração com Outros Middlewares
O trace é frequentemente usado em conjunto com jsonResponse, que automaticamente adiciona o requestId às respostas JSON.
6. validator (validateRequest)
Middleware para validação de requisições usando schemas Joi.
Funcionamento
- Valida o body da requisição contra um schema Joi
- Retorna erro 400 com detalhes da validação se houver falha
- Permite que a requisição prossiga apenas se a validação passar
Exemplo de Uso
import Koa from 'koa';
import Router from 'koa-router';
import bodyParser from 'koa-bodyparser';
import Joi from 'joi';
import { validator } from 'rivia-koa-middlewares';
const app = new Koa();
const router = new Router();
app.use(bodyParser());
// Schema de validação
const userSchema = Joi.object({
username: Joi.string().min(3).max(30).required(),
email: Joi.string().email().required(),
age: Joi.number().integer().min(18).max(120).optional()
}).unknown(false); // Rejeita campos não definidos no schema
const loginSchema = Joi.object({
username: Joi.string().required(),
password: Joi.string().min(6).required()
});
// Aplicando validação
router.post('/users', validator(userSchema), async (ctx) => {
// ctx.request.body já está validado aqui
const user = await createUser(ctx.request.body);
ctx.body = { id: user.id, message: 'Usuário criado' };
});
router.post('/login', validator(loginSchema), async (ctx) => {
const token = await authenticate(ctx.request.body);
ctx.body = { token };
});
app.use(router.routes());
app.listen(3000);Resposta de Erro
Quando a validação falha, o middleware retorna:
{
"message": "Invalid request",
"details": [
{
"message": "\"username\" is required",
"path": ["username"],
"type": "any.required"
}
]
}Status HTTP: 400 Bad Request
Exemplo Completo de Integração
Aqui está um exemplo completo usando múltiplos middlewares juntos:
import Koa from 'koa';
import Router from 'koa-router';
import bodyParser from 'koa-bodyparser';
import Joi from 'joi';
import {
trace,
inOutLogger,
authorizer,
validator,
idempotence,
jsonResponse
} from 'rivia-koa-middlewares';
const app = new Koa();
const router = new Router();
// Middlewares globais (ordem importa!)
app.use(bodyParser());
app.use(trace); // 1. Gera requestId
app.use(inOutLogger); // 2. Logs de requisição/resposta
app.use(jsonResponse); // 3. Formatação JSON e tratamento de erros
// Rota pública com validação
const loginSchema = Joi.object({
email: Joi.string().email().required(),
password: Joi.string().required()
});
router.post('/login', validator(loginSchema), async (ctx) => {
ctx.body = { token: 'jwt-token-here' };
});
// Rota protegida com autenticação
router.get('/profile', authorizer(), async (ctx) => {
ctx.body = {
user: ctx.state.user,
message: 'Perfil do usuário'
};
});
// Rota protegida com autenticação e grupo específico
router.get('/admin/users', authorizer({ groups: ['admin'] }), async (ctx) => {
ctx.body = { users: [] };
});
// Rota com idempotência (útil para pagamentos, criação de recursos)
const orderSchema = Joi.object({
items: Joi.array().items(Joi.object({
productId: Joi.string().required(),
quantity: Joi.number().integer().min(1).required()
})).min(1).required()
});
router.post('/orders',
authorizer(), // Requer autenticação
validator(orderSchema), // Valida o body
idempotence(60), // Idempotência de 60 segundos
async (ctx) => {
const order = await createOrder({
userId: ctx.state.user.sub,
items: ctx.request.body.items
});
ctx.body = { orderId: order.id, status: 'created' };
}
);
app.use(router.routes());
app.use(router.allowedMethods());
app.listen(3000, () => {
console.log('Servidor rodando na porta 3000');
});Ordem Recomendada de Middlewares
A ordem dos middlewares é importante. Segue uma ordem recomendada:
bodyParser(do koa-bodyparser) - Parse do bodytrace- Gera requestIdinOutLogger- Logging (deve vir antes de outros middlewares que podem modificar ctx)authorizer- Autenticação (se necessário)validator- Validação (se necessário)idempotence- Idempotência (se necessário)- Rotas da aplicação
jsonResponse- Formatação final (pode vir antes ou depois das rotas, mas geralmente depois)
Dependências
aws-jwt-verify: Validação de tokens JWT do Cognitojoi: Validação de schemaslodash: Utilitáriosluxon: Manipulação de datasobject-hash: Geração de hash para idempotênciarivia-dynamodb: Cliente DynamoDBrivia-logs: Sistema de logginguuid: Geração de UUIDs
Licença
ISC
