Menu
@dexcode/helpers
HttpClient e Gateway
Como funciona
Componente / service
│ http.get({ url: "/ws/01001000/json" })
▼
HttpClient ──► /api/gateway/viacep/ws/01001000/json (browser → app, mesma origem)
│
▼ src/app/api/gateway/[service]/[...path]/route.ts
Gateway ──► https://viacep.com.br/ws/01001000/json (servidor → servidor)- O HttpClient roda onde o seu código roda (normalmente no browser). Ele só conhece o nome do serviço.
- O Gateway roda no servidor, dentro de um route handler. Ele conhece as URLs reais (variáveis de ambiente) e faz a chamada para a API.
- O browser nunca envia nada direto para a API. Por isso a chamada não passa por CORS, e a API recebe a requisição vinda do servidor.
Estrutura no app
Ao final do passo a passo, o app terá estes arquivos:
.env URLs das APIs
src/
config/
gateway.ts cadastro dos serviços
app/
api/
gateway/
[service]/
[...path]/
route.ts rota que repassa para a API
services/
ViaCepService.ts chamadas da API usando o HttpClient1. Instale
npm install @dexcode/helpers2. Coloque as URLs das APIs no .env
VIACEP_SERVICE_URL=https://viacep.com.br
DEXCODE_ADDRESS_SERVICE_URL=http://localhost:5000NEXT_PUBLIC_. Essas variáveis só são lidas no servidor, e sem o prefixo a URL da API não vai para o JavaScript do browser.3. Cadastre os serviços em src/config/gateway.ts
Cada chave de services é o nome do serviço, usado na URL do gateway e no HttpClient. O declare module no final faz o editor sugerir esses nomes (Ctrl+Espaço) e acusar erro em nomes que não existem.
import { Gateway } from "@dexcode/helpers";
const services = {
viacep: process.env.VIACEP_SERVICE_URL,
dexcode_address_service: process.env.DEXCODE_ADDRESS_SERVICE_URL,
};
export const gateway = new Gateway({ services });
// Autocomplete do "service" no HttpClient
declare module "@dexcode/helpers" {
interface GatewayRegistry {
services: typeof services;
}
}Para adicionar uma API nova, crie a variável no .env e uma linha em services. Ela já passa a aparecer no autocomplete.
4. Crie a rota do gateway
Crie o arquivo exatamente neste caminho: src/app/api/gateway/[service]/[...path]/route.ts
import { gateway } from "@/config/gateway";
const handler = gateway.handler;
export {
handler as GET,
handler as POST,
handler as PUT,
handler as PATCH,
handler as DELETE,
};[service], no singular. O Gateway lê o parâmetro com esse nome, então [services] ou outro nome faz toda chamada responder 404. O [...path] pega o resto do caminho, com quantos níveis tiver.Para conferir, abra no browser ou no terminal:
curl http://localhost:3000/api/gateway/viacep/ws/01001000/json5. Crie o service
O HttpClient recebe só o nome do serviço. Os caminhos (url) são relativos à URL cadastrada no .env.
import { HttpClient } from "@dexcode/helpers";
export interface Endereco {
cep: string;
logradouro: string;
bairro: string;
localidade: string;
uf: string;
}
class ViaCepService {
private http = new HttpClient({ service: "viacep" });
getEnderecoByCep(cep: string) {
return this.http.get<Endereco>({ url: `/ws/${cep}/json` });
}
}
export const viaCepService = new ViaCepService();6. Use no componente
"use client";
import { useState } from "react";
import { HttpClientError } from "@dexcode/helpers";
import { viaCepService, type Endereco } from "@/services/ViaCepService";
export function BuscaCep() {
const [endereco, setEndereco] = useState<Endereco | null>(null);
const [erro, setErro] = useState("");
async function buscar(cep: string) {
try {
setEndereco(await viaCepService.getEnderecoByCep(cep));
setErro("");
} catch (error) {
if (error instanceof HttpClientError) {
setErro(`A API respondeu ${error.status}`);
}
}
}
return (
<div>
<input placeholder="CEP" onBlur={(e) => buscar(e.target.value)} />
{endereco && <p>{endereco.logradouro} - {endereco.localidade}/{endereco.uf}</p>}
{erro && <p>{erro}</p>}
</div>
);
}Requisições
Todos os métodos (get, post, put, patch, delete) recebem as mesmas opções e retornam o body já convertido: JSON quando a API responde JSON, senão texto.
const http = new HttpClient({ service: "dexcode_address_service" });
// GET com query string
const lista = await http.get<Endereco[]>({ url: "/enderecos?cidade=Recife" });
// POST com JSON (padrão)
await http.post({ url: "/enderecos", data: { cep: "01001000", numero: "10" } });
// PUT / PATCH / DELETE
await http.put({ url: "/enderecos/1", data: { numero: "20" } });
await http.patch({ url: "/enderecos/1", data: { numero: "30" } });
await http.delete({ url: "/enderecos/1" });
// Formulário
await http.post({ url: "/login", data: { usuario: "ana" }, contentType: "application/x-www-form-urlencoded" });
// Upload: FormData vai como está, o boundary é gerado automaticamente
const form = new FormData();
form.append("arquivo", file);
await http.post({ url: "/anexos", data: form });
// Headers extras e outras opções do fetch
await http.get({ url: "/privado", config: { headers: { Authorization: `Bearer ${token}` } } });Com o retorno no formato padrão das APIs DexCode, use o DefaultResponse do @dexcode/types:
import type { DefaultResponse } from "@dexcode/types";
const response = await http.get<DefaultResponse<Endereco[]>>({ url: "/enderecos" });Erros
- Resposta que não é 2xx: o
HttpClientlançaHttpClientError, comstatusebody(a resposta da API já convertida). - Serviço não cadastrado ou variável de ambiente vazia: o Gateway responde
404. - API fora do ar ou URL errada: o Gateway responde
502. - Qualquer outra resposta da API é repassada como veio: mesmo status e mesmo body.
Chamando a API direto (serverSide: false)
Por padrão toda chamada passa pelo gateway. Com serverSide: false, o browser chama a API direto, sem passar pelo servidor Next. Isso só funciona se a API aceitar CORS do seu domínio, e exige uma baseUrl pública:
const http = new HttpClient({
service: "viacep",
baseUrl: process.env.NEXT_PUBLIC_VIACEP_SERVICE_URL,
});
await http.get({ url: "/ws/01001000/json", serverSide: false });serverSide: false.Em Server Components e server actions
O gatewayUrl padrão (/api/gateway) é relativo, e no servidor o fetch não aceita URL relativa. Se um service for chamado no servidor, informe a URL completa do app:
const http = new HttpClient({
service: "viacep",
gatewayUrl: `${process.env.APP_URL}/api/gateway`, // ex.: http://localhost:3000
});Nesse caso a chamada vai do servidor Next para ele mesmo e só depois para a API. Se o código roda apenas no servidor, também dá para pular essa volta e montar a URL com gateway.resolveUrl("viacep", "/ws/01001000/json").
Opções do Gateway
| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| services* | Record<string, string | undefined> | — | Nome do serviço → URL base da API. Use variáveis de ambiente sem NEXT_PUBLIC_. |
| forwardedHeaders | string[] | ["content-type", "accept", "authorization"] | Headers do browser repassados para a API. Os demais (cookies, origin...) não são enviados. |
Além do handler usado no route.ts, o Gateway expõe resolveUrl(service, path), que monta a URL da API (ou retorna null se o serviço não existe), e forward(request, service, path), que faz o repasse.
Opções do HttpClient
| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| service* | GatewayServiceName | — | Nome do serviço cadastrado no Gateway. Com o GatewayRegistry preenchido, o editor sugere os nomes. |
| gatewayUrl | string | "/api/gateway" | URL da rota do gateway. Precisa ser absoluta quando a chamada roda no servidor. |
| baseUrl | string | — | URL da API, usada só nas chamadas com serverSide: false. |
Opções de cada requisição
| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| url | string | "" | Caminho na API, com query string se precisar. Ex.: /ws/01001000/json |
| data | unknown | — | Body. Objetos viram JSON (ou form-urlencoded); FormData e Blob vão como estão. |
| contentType | ContentType | "application/json" | Content-Type do body. Tipo exportado pelo @dexcode/types. |
| serverSide | boolean | true | true passa pelo gateway; false chama a API direto do browser em baseUrl. |
| config | RequestInit | — | Opções extras do fetch (headers, signal, cache...). method e body são ignorados. |
Segurança
Authorization, que é repassado). Se a rota /api/gateway for protegida pelo createProxy, só usuários logados conseguem usá-la.