@ecdt/server-common
v1.4.0
Published
Conjunto de ferramentas e configurações comuns nos servidores da Econodata
Keywords
Readme
@ecdt/server-common
Conjunto de ferramentas e configurações comuns para servidores Express da Econodata.
O que esta biblioteca faz
@ecdt/server-common fornece um conjunto de middlewares pré-configurados para aplicações Express, eliminando a necessidade de configurar manualmente o parsing de requisições e a sanitização de tokens em cada serviço.
expressCommonMiddlewares(options?)
Retorna um array com os seguintes middlewares já configurados, prontos para serem aplicados no Express via app.use():
| Middleware | Descrição |
|---|---|
| bodyParser.text() | Faz o parse de corpos de requisição como texto puro |
| bodyParser.json() | Faz o parse de corpos de requisição no formato JSON |
| bodyParser.urlencoded({ extended: false }) | Faz o parse de corpos no formato application/x-www-form-urlencoded |
| devMktTokenSanitaze | Sanitiza o token de autenticação recebido no header Authorization ou no cookie de sessão |
Sobre o devMktTokenSanitaze
Este middleware normaliza tokens de autenticação que chegam codificados na URL (substituindo + e %2B por espaço). Ele verifica:
- O header
Authorization— se presente, sanitiza o valor diretamente. - O cookie da requisição — se não houver
Authorization, extrai o token do cookie de sessão e o define emreq.headers.authorization.
Resolução do nome do cookie de sessão
Por padrão, o nome do cookie é resolvido por requisição, a partir da origem (Origin > Referer > Host):
- Origem com subdomínio
hml(ex:hml.econodata.com.br,api.hml.econodata.com.br,hml-site.econodata.com.br) → cookiehml-ecdt_token_site - Demais origens → cookie
ecdt_token_site
Em ambos os casos, se o cookie preferido não existir na requisição, o outro nome é usado como fallback. A busca é por match exato do nome do cookie.
Também é possível fixar o nome explicitamente via options.cookieName (string ou array em ordem de preferência), ignorando a detecção por origem:
// Nome fixo
app.use(...expressCommonMiddlewares({ cookieName: "hml-ecdt_token_site" }));
// Múltiplos nomes, em ordem de preferência
app.use(...expressCommonMiddlewares({ cookieName: ["hml-ecdt_token_site", "ecdt_token_site"] }));expressCors(options)
Retorna o middleware de CORS padrão dos servidores Econodata (baseado no pacote cors).
| Opção | Tipo | Descrição |
|---|---|---|
| origins | string[] | Origins permitidas. Requisições de outras origins não recebem os headers CORS (o navegador bloqueia a resposta). Obrigatório se econodataOrigins não for true |
| econodataOrigins | boolean | Permite todas as origins https do domínio econodata.com.br (qualquer subdomínio, ex: https://app.econodata.com.br, https://api.hml.econodata.com.br) |
| extraHeaders | string[] | Headers adicionais além dos padrões |
| headers | string[] | Lista exata de headers permitidos — substitui os padrões. Não combinar com extraHeaders |
| methods | string[] | Lista exata de métodos permitidos. Default: todos (GET, HEAD, PUT, PATCH, POST, DELETE, OPTIONS) |
- Headers padrões:
Authorization,Content-Type,X-Requested-With - Credentials: sempre habilitado (
Access-Control-Allow-Credentials: true), por isso a origin permitida é sempre ecoada — nunca*
const { expressCors } = require("@ecdt/server-common");
// Configuração mínima
app.use(expressCors({ origins: ["https://app.econodata.com.br"] }));
// Todas as origins do domínio econodata.com.br (qualquer subdomínio)
app.use(expressCors({ econodataOrigins: true }));
// Combinando econodataOrigins com origins extras
app.use(expressCors({ econodataOrigins: true, origins: ["http://localhost:3000"] }));
// Headers extras além dos padrões
app.use(expressCors({ origins: [...], extraHeaders: ["X-Custom-Header"] }));
// Lista exata de headers e métodos
app.use(expressCors({ origins: [...], headers: ["Content-Type"], methods: ["GET", "POST"] }));setupRedMetrics(app, options?)
Instrumenta o servidor com métricas RED (Rate, Errors, Duration) e expõe o endpoint /metrics no formato Prometheus, em uma linha. Todas as requisições passam a alimentar o histograma http_request_duration_seconds{method, route, status_code}.
Requer
prom-clientinstalado no microsserviço (é umapeerDependency).
| Opção | Tipo | Descrição |
|---|---|---|
| serviceName | string | Nome do MS; vira o label service em todas as métricas. Fallback: env SERVICE_NAME > npm_package_name |
| metricsPath | string | Caminho do endpoint de métricas. Default: /metrics |
| buckets | number[] | Buckets de latência (segundos). Default: [0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10] |
| collectDefaultMetrics | boolean | Coletar métricas default do Node (heap, event loop, GC...). Default: true |
| registry | Registry | Registry alternativo do prom-client. Default: registry global |
const express = require("express");
const { setupRedMetrics } = require("@ecdt/server-common");
const app = express();
// ANTES de montar as rotas:
setupRedMetrics(app, { serviceName: "dev-mkt-busca" });
app.get("/users/:id", (req, res) => res.json({ id: req.params.id }));
app.listen(3000);O label route usa o template da rota (/users/:id), nunca a URL crua — isso mantém a cardinalidade (e o custo de ingestão) sob controle.
Consultas (PromQL) habilitadas
# Latência p95 por endpoint
histogram_quantile(0.95, sum by (le, route)(rate(http_request_duration_seconds_bucket{service="dev-mkt-busca"}[5m])))
# Taxa de requisições por segundo (RPS)
sum(rate(http_request_duration_seconds_count{service="dev-mkt-busca"}[1m]))
# Erros 5xx por segundo
sum(rate(http_request_duration_seconds_count{service="dev-mkt-busca",status_code=~"5.."}[5m]))
# Endpoints que mais geram erro
topk(10, sum by (route)(rate(http_request_duration_seconds_count{service="dev-mkt-busca",status_code=~"5.."}[5m])))Para wiring manual, a lib também exporta redMetricsMiddleware(options?) e metricsHandler(registry?).
Instalação
npm install @ecdt/server-commonUso
const express = require("express");
const { expressCommonMiddlewares } = require("@ecdt/server-common");
const app = express();
app.use(...expressCommonMiddlewares());
app.get("/health", (req, res) => {
res.json({ status: "ok" });
});
app.listen(3000);