---
title: "gRPC-Gateway em Go: REST e gRPC com o Mesmo Protobuf"
url: "https://golang.com.br/blog/grpc-gateway-go-rest-protobuf/"
markdown_url: "https://golang.com.br/blog/grpc-gateway-go-rest-protobuf.MD"
description: "Aprenda gRPC-Gateway em Go: gere uma API REST/JSON a partir do Protobuf e configure Buf, erros, autenticação, OpenAPI, testes e deploy em produção."
date: "2026-08-13"
author: "Golang Brasil"
---

# gRPC-Gateway em Go: REST e gRPC com o Mesmo Protobuf

Aprenda gRPC-Gateway em Go: gere uma API REST/JSON a partir do Protobuf e configure Buf, erros, autenticação, OpenAPI, testes e deploy em produção.


**Resposta rápida:** use **gRPC-Gateway em Go** quando o serviço já tem contratos Protobuf e precisa atender também clientes que falam HTTP/JSON. Você anota os métodos gRPC com `google.api.http`, gera um `runtime.ServeMux` e publica rotas REST como `GET /v1/users/{id}` sem manter DTOs e handlers duplicados. A solução funciona melhor quando gRPC é o contrato principal; se a API pública tem semântica, payloads ou ciclo de vida muito diferentes, mantenha uma camada REST própria.

gRPC é uma ótima escolha para comunicação interna entre serviços, mas browsers, integrações externas, ferramentas de linha de comando e equipes parceiras frequentemente preferem REST com JSON. Criar as duas interfaces manualmente costuma gerar drift: o campo muda no `.proto`, mas não no DTO HTTP; um código de erro é corrigido no gRPC, mas continua diferente no REST; a documentação deixa de representar a implementação.

O **gRPC-Gateway** reduz essa duplicação ao gerar uma camada de transcodificação. A requisição HTTP entra pelo gateway, vira uma mensagem Protobuf, chama o serviço gRPC e volta como JSON. Este guia mostra a arquitetura, um exemplo completo, geração com Buf, autenticação, erros, OpenAPI, testes e critérios para decidir entre gRPC-Gateway, REST manual e ConnectRPC.

Se você ainda não domina a base, leia primeiro [gRPC em Go com Protobuf](/blog/grpc-go-protobuf-apis-internas/) e o tutorial completo de [Go e gRPC](/tutoriais/go-grpc-tutorial/). Para APIs puramente HTTP, o caminho mais simples continua sendo [API REST com Go e net/http](/aprenda/api-rest-go/).

## Quando usar gRPC-Gateway

| Cenário | Recomendação |
|---|---|
| Serviço interno já usa gRPC e precisa expor JSON | **gRPC-Gateway** |
| Um contrato Protobuf deve gerar gRPC, REST e OpenAPI | **gRPC-Gateway** |
| API pública tem recursos e payloads muito diferentes do RPC interno | REST separado |
| Projeto pequeno só precisa de HTTP/JSON | `net/http`, Chi, Gin ou Echo |
| Mesmo handler deve falar Connect, gRPC e gRPC-Web | Avalie **ConnectRPC** |
| Gateway precisa escalar e receber políticas de borda próprias | Processo separado |
| Time quer deploy simples para uma aplicação pequena | Gateway no mesmo binário |

O principal sinal favorável é a existência de uma fonte de verdade em Protobuf. Se você ainda não usa gRPC e só quer gerar uma API REST, começar por OpenAPI pode ser mais direto; veja [OpenAPI com oapi-codegen](/blog/openapi-go-oapi-codegen-contratos/).

## Como o gRPC-Gateway funciona

A arquitetura mais comum tem três etapas:

```text
Cliente HTTP/JSON
       |
       | GET /v1/users/42
       v
runtime.ServeMux (gRPC-Gateway)
       |
       | user.v1.UserService/GetUser
       v
Servidor gRPC-Go
       |
       v
Serviço de domínio + banco
```

O código gerado conhece o mapeamento entre rota HTTP e método gRPC. O gateway não deve conter regra de negócio: ele traduz transporte, propaga metadata e converte respostas. Validação de domínio, autorização e acesso a dados continuam no serviço.

Você pode executar gateway e gRPC no mesmo processo, em portas diferentes, ou publicar cada um como deployment independente. A separação é útil quando a borda HTTP precisa de WAF, rate limit, CORS ou escala diferente. O mesmo processo reduz componentes para operar e funciona bem em sistemas menores.

## Definindo a rota REST no Protobuf

Considere um serviço de usuários em `proto/user/v1/user.proto`:

```protobuf
syntax = "proto3";

package user.v1;

option go_package = "example.com/accounts/gen/user/v1;userv1";

import "google/api/annotations.proto";

service UserService {
  rpc GetUser(GetUserRequest) returns (GetUserResponse) {
    option (google.api.http) = {
      get: "/v1/users/{id}"
    };
  }

  rpc CreateUser(CreateUserRequest) returns (CreateUserResponse) {
    option (google.api.http) = {
      post: "/v1/users"
      body: "*"
    };
  }
}

message GetUserRequest {
  string id = 1;
}

message GetUserResponse {
  string id = 1;
  string name = 2;
  string email = 3;
}

message CreateUserRequest {
  string name = 1;
  string email = 2;
}

message CreateUserResponse {
  string id = 1;
  string name = 2;
  string email = 3;
}
```

A anotação `get: "/v1/users/{id}"` liga o parâmetro de caminho ao campo `id`. No `POST`, `body: "*"` informa que o corpo JSON preenche a mensagem inteira. Para requests com metadata fora do corpo, você pode apontar `body` para um campo específico.

Evite desenhar a rota como tradução mecânica do nome do RPC, por exemplo `/callGetUser`. Mesmo usando RPC por baixo, a interface HTTP deve respeitar convenções reconhecíveis: recursos no plural, métodos HTTP coerentes, query string para filtros e status adequados.

## Geração reproduzível com Buf

É possível chamar `protoc` diretamente, mas **Buf** facilita dependências, lint, breaking-change detection e geração. Um `buf.yaml` mínimo:

```yaml
version: v2
modules:
  - path: proto
deps:
  - buf.build/googleapis/googleapis
lint:
  use:
    - STANDARD
breaking:
  use:
    - FILE
```

O `buf.gen.yaml` declara os plugins:

```yaml
version: v2
plugins:
  - remote: buf.build/protocolbuffers/go
    out: gen
    opt: paths=source_relative
  - remote: buf.build/grpc/go
    out: gen
    opt: paths=source_relative
  - remote: buf.build/grpc-ecosystem/gateway
    out: gen
    opt: paths=source_relative
  - remote: buf.build/grpc-ecosystem/openapiv2
    out: gen/openapi
```

Gere os arquivos com:

```bash
buf dep update
buf lint
buf generate
```

O exemplo omite versões dos plugins para manter a leitura curta. No repositório real, fixe as versões aprovadas no `remote` ou use plugins locais instalados por uma ferramenta versionada; sem isso, duas gerações em datas diferentes podem produzir diffs inesperados.

O repositório passa a receber, entre outros artefatos, o código Protobuf, o servidor/cliente gRPC e o arquivo `*.pb.gw.go` do gateway. No CI, execute `buf lint`, verifique breaking changes conforme a política do time, rode `buf generate` e falhe se os arquivos versionados ficarem diferentes:

```bash
buf lint
buf breaking --against '.git#branch=main'
buf generate
git diff --exit-code -- gen
```

Fixe a configuração e revise atualizações de plugins. Usar `@latest` de forma implícita em cada máquina pode gerar diffs inesperados e builds pouco reproduzíveis.

## Subindo o servidor gRPC

O servidor continua sendo gRPC-Go normal. A implementação abaixo valida a entrada e traduz erros de domínio para códigos gRPC:

```go
package transport

import (
    "context"

    userv1 "example.com/accounts/gen/user/v1"
    "google.golang.org/grpc/codes"
    "google.golang.org/grpc/status"
)

type UserService interface {
    FindByID(ctx context.Context, id string) (User, error)
}

type UserServer struct {
    userv1.UnimplementedUserServiceServer
    users UserService
}

func (s *UserServer) GetUser(
    ctx context.Context,
    req *userv1.GetUserRequest,
) (*userv1.GetUserResponse, error) {
    if req.GetId() == "" {
        return nil, status.Error(codes.InvalidArgument, "id é obrigatório")
    }

    user, err := s.users.FindByID(ctx, req.GetId())
    if err != nil {
        if IsNotFound(err) {
            return nil, status.Error(codes.NotFound, "usuário não encontrado")
        }
        return nil, status.Error(codes.Internal, "erro ao buscar usuário")
    }

    return &userv1.GetUserResponse{
        Id:    user.ID,
        Name:  user.Name,
        Email: user.Email,
    }, nil
}
```

A aplicação registra esse servidor em um `grpc.Server`, configura TLS quando necessário e aplica interceptors de autenticação, logs, métricas e tracing. O guia de [OpenTelemetry em Go](/blog/go-opentelemetry-observabilidade-tracing-metricas/) mostra como conectar a chamada HTTP à operação gRPC e ao banco.

## Subindo o gateway HTTP

O gateway gerado registra as rotas em um `runtime.ServeMux`:

```go
package main

import (
    "context"
    "log"
    "net/http"
    "time"

    userv1 "example.com/accounts/gen/user/v1"
    "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials/insecure"
)

func runGateway(ctx context.Context) error {
    mux := runtime.NewServeMux()

    opts := []grpc.DialOption{
        grpc.WithTransportCredentials(insecure.NewCredentials()),
    }

    if err := userv1.RegisterUserServiceHandlerFromEndpoint(
        ctx,
        mux,
        "127.0.0.1:9090",
        opts,
    ); err != nil {
        return err
    }

    server := &http.Server{
        Addr:              ":8080",
        Handler:           mux,
        ReadHeaderTimeout: 5 * time.Second,
        ReadTimeout:       15 * time.Second,
        WriteTimeout:      15 * time.Second,
        IdleTimeout:       60 * time.Second,
    }

    log.Println("HTTP/JSON em :8080")
    return server.ListenAndServe()
}
```

Em produção, não use credenciais inseguras para uma conexão que atravessa rede não confiável. Se gateway e gRPC estão no mesmo pod ou host, a decisão depende do modelo de ameaça; entre deployments, use TLS ou mTLS e valide nomes e certificados.

Para uma implantação no mesmo processo, você também pode registrar o handler diretamente contra a implementação, evitando uma conexão de rede local. A alternativa via endpoint é mais fácil de separar depois e exercita o caminho gRPC real.

A rota já pode ser chamada com:

```bash
curl -sS http://localhost:8080/v1/users/42
```

Resposta:

```json
{
  "id": "42",
  "name": "Ana Souza",
  "email": "ana@example.com"
}
```

## Erros HTTP e gRPC sem inconsistência

Por padrão, o gateway converte códigos gRPC para status HTTP. Exemplos esperados incluem:

| Código gRPC | HTTP típico |
|---|---:|
| `InvalidArgument` | 400 |
| `Unauthenticated` | 401 |
| `PermissionDenied` | 403 |
| `NotFound` | 404 |
| `AlreadyExists` | 409 |
| `ResourceExhausted` | 429 |
| `Unavailable` | 503 |
| `Internal` | 500 |

Não retorne todo erro como `Internal`. O consumidor precisa distinguir entrada inválida, ausência de recurso, falta de autenticação e indisponibilidade transitória. Ao mesmo tempo, não envie detalhes de SQL, stack trace ou token na mensagem pública.

Se sua API precisa de um envelope de erro próprio, configure `runtime.WithErrorHandler`. Faça isso com cuidado: preserve o status correto, mantenha um formato estável e teste todos os códigos relevantes. Um corpo possível:

```json
{
  "code": "USER_NOT_FOUND",
  "message": "usuário não encontrado",
  "request_id": "req_01J..."
}
```

O código de domínio não deve depender do texto da mensagem. Mensagens mudam e podem ser traduzidas; códigos estáveis podem alimentar UI, retry e métricas.

## Autenticação, headers e metadata

Uma requisição REST costuma trazer `Authorization`, `X-Request-ID` e informações de rastreamento em headers. O serviço gRPC recebe metadata. Use um matcher para controlar o que pode atravessar a fronteira, em vez de encaminhar qualquer header automaticamente.

Uma política comum é:

- aceitar `Authorization` e converter para metadata;
- gerar ou propagar `X-Request-ID`;
- propagar contexto de tracing pelos padrões do OpenTelemetry;
- bloquear headers internos que o cliente não deveria definir;
- nunca registrar credenciais em logs.

Autenticação responde “quem é você?”; autorização responde “você pode acessar este usuário?”. O gateway pode validar token e aplicar políticas de borda, mas a autorização ligada ao recurso deve continuar protegida no serviço. Leia [autenticação e autorização em APIs Go](/blog/autenticacao-autorizacao-go-apis/) para estruturar essa separação.

## OpenAPI gerada: útil, mas não automática demais

O plugin `protoc-gen-openapiv2` gera um documento Swagger/OpenAPI v2 a partir do contrato. Isso acelera documentação, clientes e testes, mas o arquivo bruto raramente é suficiente para uma API pública.

Revise e enriqueça:

- título, descrição e versão;
- exemplos realistas de request e response;
- esquemas de autenticação;
- códigos de erro e seus corpos;
- paginação, filtros e limites;
- depreciação de campos e rotas;
- contato e política de compatibilidade.

Valide a especificação no CI e publique uma interface como Swagger UI ou Redoc apenas se ela estiver protegida conforme a sensibilidade do serviço. Para uma estratégia contract-first centrada em HTTP, compare com [oapi-codegen em Go](/blog/openapi-go-oapi-codegen-contratos/).

## JSON, nomes de campos e valores ausentes

Protobuf e JSON não têm semântica idêntica. Antes de publicar o contrato, teste:

- nomes em `snake_case` no `.proto` e sua forma JSON;
- enums desconhecidos;
- campos opcionais e presença;
- números inteiros grandes;
- timestamps e durations;
- bytes em base64;
- valores zero omitidos ou emitidos;
- campos desconhecidos enviados pelo cliente.

Você pode customizar marshaling com `runtime.WithMarshalerOption`, usando `runtime.JSONPb` e opções de `protojson`. Não altere opções por preferência estética sem avaliar compatibilidade. Emitir campos zero, por exemplo, muda o payload e pode afetar clientes, snapshots e cache.

## Testes que realmente reduzem risco

Teste as três camadas, não apenas o método de domínio.

### 1. Teste do serviço gRPC

Chame o handler diretamente ou use `bufconn` para verificar serialização, interceptors, metadata e códigos gRPC.

### 2. Teste do gateway HTTP

Registre o mux gerado em `httptest.NewServer` e faça uma chamada JSON real:

```go
func TestGetUserHTTP(t *testing.T) {
    server := httptest.NewServer(gatewayHandler(t))
    t.Cleanup(server.Close)

    resp, err := http.Get(server.URL + "/v1/users/42")
    if err != nil {
        t.Fatal(err)
    }
    defer resp.Body.Close()

    if resp.StatusCode != http.StatusOK {
        t.Fatalf("status = %d", resp.StatusCode)
    }

    var got struct {
        ID   string `json:"id"`
        Name string `json:"name"`
    }
    if err := json.NewDecoder(resp.Body).Decode(&got); err != nil {
        t.Fatal(err)
    }
    if got.ID != "42" || got.Name == "" {
        t.Fatalf("resposta inválida: %+v", got)
    }
}
```

### 3. Teste de compatibilidade

Use `buf breaking` para detectar remoção de campos, troca de tipo e mudanças incompatíveis no contrato. Mantenha testes de snapshot do OpenAPI apenas quando o time revisa o diff conscientemente; snapshots aceitos no automático só registram a quebra.

Quando o serviço acessa PostgreSQL, Redis ou outra dependência, combine o teste de transporte com [Testcontainers em Go](/blog/go-testcontainers-testes-integracao-containers/).

## Produção: timeouts, observabilidade e shutdown

O gateway adiciona uma fronteira, não uma licença para esquecer operação. Configure:

- timeouts no `http.Server` e deadlines nas chamadas gRPC;
- limites de corpo e tamanho de mensagem;
- CORS apenas para origens e métodos necessários;
- rate limit por identidade ou consumidor;
- logs estruturados sem payload sensível;
- métricas separando rota HTTP, método gRPC, status e latência;
- tracing entre gateway, serviço, banco e integrações;
- readiness para evitar tráfego antes das dependências estarem prontas;
- graceful shutdown para drenar HTTP e gRPC.

Evite usar IDs de usuário, e-mail ou URL completa como label de Prometheus. Prefira o template da rota, como `/v1/users/{id}`, para impedir cardinalidade sem limite. Os guias de [health checks em Go](/blog/health-checks-go-liveness-readiness-startup/) e [graceful shutdown](/blog/graceful-shutdown-go-producao/) cobrem essa base.

## gRPC-Gateway vs REST manual vs ConnectRPC

| Critério | gRPC-Gateway | REST manual | ConnectRPC |
|---|---|---|---|
| Fonte principal | Protobuf | Código/OpenAPI | Protobuf |
| REST/JSON | Via transcodificação | Nativo e customizável | Protocolo HTTP amigável |
| gRPC | Sim | Só se implementar separado | Sim |
| OpenAPI | Plugin disponível | Ferramentas próprias | Fluxo depende da integração |
| Controle do payload HTTP | Médio | Máximo | Médio |
| Duplicação de handlers | Baixa | Pode ser alta | Baixa |
| Melhor encaixe | Ecossistema gRPC-Go existente | API pública HTTP-first | Serviços novos multi-protocolo |

**Escolha gRPC-Gateway** quando Protobuf já governa o contrato e clientes HTTP são uma interface adicional. **Escolha REST manual** quando HTTP é o produto principal e precisa de representação muito específica, caching, ETags, uploads ou semântica que não combina com o serviço RPC. **Avalie ConnectRPC** em projetos novos quando servir múltiplos protocolos pelo mesmo modelo de handler simplifica a arquitetura.

Não adote duas soluções ao mesmo tempo para “ficar preparado”. Escolha uma fronteira, documente o motivo e meça o custo operacional.

## Armadilhas comuns

1. **Anotar qualquer RPC como REST sem redesenhar a rota.** O resultado é uma API HTTP com cara de chamada de função.
2. **Expor o model interno inteiro.** Contratos públicos precisam de estabilidade; não transforme toda coluna do banco em campo Protobuf.
3. **Executar sem deadline.** Gateway e serviço podem acumular chamadas presas durante uma falha em cascata.
4. **Confiar só na documentação gerada.** OpenAPI sem exemplos, erros e autenticação continua difícil de consumir.
5. **Propagar todos os headers.** Isso cria colisões, risco de spoofing e vazamento de informação interna.
6. **Misturar validação de transporte e regra de negócio.** Formato e presença básica podem ser validados na borda; autorização e invariantes pertencem ao serviço.
7. **Ignorar compatibilidade JSON.** Uma mudança compatível em Protobuf pode surpreender um cliente que depende do payload JSON exato.
8. **Subir gateway e gRPC sem shutdown coordenado.** O processo pode aceitar HTTP enquanto o backend já está encerrando.

## Checklist antes do deploy

- [ ] Rotas usam recursos e métodos HTTP coerentes?
- [ ] `buf lint` e `buf breaking` rodam no CI?
- [ ] Plugins e geração são reproduzíveis?
- [ ] Códigos gRPC viram status HTTP corretos?
- [ ] O formato de erro é estável e não vaza detalhes?
- [ ] Headers permitidos estão em uma allowlist?
- [ ] Autenticação e autorização foram testadas separadamente?
- [ ] Timeouts existem no HTTP, gRPC e banco?
- [ ] OpenAPI contém exemplos e respostas de erro?
- [ ] Testes exercitam JSON real pelo gateway?
- [ ] Métricas usam template de rota, não IDs?
- [ ] Readiness e graceful shutdown cobrem os dois servidores?
- [ ] Existe estratégia de versionamento e depreciação?

## Perguntas frequentes

### O que é gRPC-Gateway em Go?

É um conjunto de plugins que gera um proxy HTTP/JSON a partir de serviços Protobuf. O gateway recebe REST, converte para a mensagem do método gRPC e traduz a resposta de volta para JSON.

### Ele substitui uma API REST escrita à mão?

Sim, quando as operações HTTP representam naturalmente o mesmo contrato. Se a API pública exige payloads, recursos e políticas muito diferentes do RPC interno, uma camada REST própria oferece mais controle.

### Preciso de dois servidores?

Não. Gateway e gRPC podem compartilhar um processo ou rodar separadamente. A primeira opção simplifica; a segunda permite escala e políticas de borda independentes.

### gRPC-Gateway gera OpenAPI?

Sim, por meio do `protoc-gen-openapiv2`. Trate o resultado como ponto de partida e valide descrições, exemplos, segurança e respostas no CI.

### Quando considerar ConnectRPC?

Considere ConnectRPC em serviços novos que precisam falar protocolos baseados em HTTP e gRPC com menos infraestrutura de proxy. Em uma plataforma já padronizada em gRPC-Go e `google.api.http`, gRPC-Gateway tende a exigir menos mudança.

## Conclusão

O gRPC-Gateway resolve uma dor real de plataformas Go: oferecer uma interface HTTP/JSON sem duplicar contratos, DTOs e regras em dois conjuntos de handlers. A combinação funciona melhor quando Protobuf é a fonte de verdade, rotas REST são desenhadas com intenção e o time valida compatibilidade tanto no protocolo gRPC quanto no JSON publicado.

Comece pequeno: anote um método de leitura, gere o gateway com Buf, teste a rota com `httptest` e confirme erros, metadata e OpenAPI. Depois adicione autenticação, observabilidade, breaking-change detection e shutdown coordenado. Essa progressão entrega uma API útil sem transformar o gateway em uma segunda aplicação de negócio.

Para continuar, combine este guia com [gRPC e Protobuf em produção](/blog/grpc-go-protobuf-apis-internas/), [API REST com Go](/aprenda/api-rest-go/), [OpenAPI com oapi-codegen](/blog/openapi-go-oapi-codegen-contratos/), [context e timeouts](/blog/context-timeout-cancelamento-go/) e as [vagas Go no Brasil](/vagas/).

## Referências oficiais

- [gRPC-Gateway](https://grpc-ecosystem.github.io/grpc-gateway/)
- [Repositório grpc-gateway](https://github.com/grpc-ecosystem/grpc-gateway)
- [Anotações HTTP do Google APIs](https://cloud.google.com/endpoints/docs/grpc-service-config/reference/rpc/google.api#http)
- [Buf — geração de código](https://buf.build/docs/generate/)
- [gRPC-Go](https://github.com/grpc/grpc-go)
- [Protocol Buffers para Go](https://protobuf.dev/reference/go/go-generated/)
