# HTTP/JSON

Source: https://gortexa.sheng.page/en/transports/http-json/

The HTTP/JSON surface comes from grpc-gateway: routes and payload shapes are generated from `google.api.http` annotations in the proto. No handlers are written by hand.

## Route mapping

Annotations sit on each rpc, for example in the sample resource:

```protobuf
rpc GetResource(GetResourceRequest) returns (Resource) {
  option (google.api.http) = {get: "/v1/resources/{id}"};
}
```

The full mapping for the sample resource:

| RPC | HTTP |
| --- | --- |
| CreateResource | `POST /v1/resources` |
| GetResource | `GET /v1/resources/{id}` |
| ListResources | `GET /v1/resources` |
| UpdateResource | `PATCH /v1/resources/{id}` |
| DeleteResource | `DELETE /v1/resources/{id}` |

## Calling and auth

```bash
curl localhost:8080/v1/resources/x \
  -H 'Authorization: Bearer <token>'
```

Without a token the response is 401. The gateway shares the interceptor chain with gRPC and MCP, and error bodies come from the unified error model — internal causes are not included.

## Request body limits

The gateway caps request bodies at 1 MiB with a 30-second read timeout: oversized bodies get 413, slow reads get 408.

## CORS and OpenAPI

- `server.enable_cors: true` is only the master switch; the actual allowlist is `server.cors_origins`. Origins not listed there receive no CORS headers at all — enabling `enable_cors` without setting origins still blocks browsers. Use `"*"` to allow all, or list explicit origins (`GORTEXA_SERVER__CORS_ORIGINS` accepts a comma-separated override). Preflight OPTIONS requests are short-circuited.
- With `server.openapi: true`, the OpenAPI spec is served at `/openapi.json`. The spec is a build artifact of `make gen`, read once at startup from `gen/openapiv2/gortexa.swagger.json`, relative to the working directory, and served from memory. When that file is missing, `/openapi.json` answers 404 `openapi spec unavailable`. A container image that needs the endpoint must ship the file at that path under its `WORKDIR`.

## Request headers

The gateway forwards `Authorization` and `X-Request-Id` onto fixed metadata keys and refuses any `Grpc-Metadata-*` header that would collide with a trusted key (`authorization`, `x-request-id`, `x-gortexa-peer-ip`) — a client cannot smuggle a second value onto its own token, request id or rate-limit identity. Separately, `Authorization`, `X-Forwarded-For` and `X-Forwarded-Host` are the three headers grpc-gateway splices into outgoing metadata without a validity check of its own, so the gateway filters them: any value outside printable ASCII is dropped rather than forwarded. Left in place, one such byte would make the loopback fail with `internal` **before** the interceptor chain ran, handing the caller an opaque 500 with no request id instead of the chain's real verdict — and letting an anonymous caller drive the server's internal-error rate.

## Response headers

Response headers pass through an allowlist: internal headers do not leak, and `X-Request-Id` is preserved for correlation.

## Related

- [httpcompat component](https://gortexa.sheng.page/en/components/http-mcp/): the API that builds the gateway mux
- [Core concepts](https://gortexa.sheng.page/en/concepts/): how the error model decides the HTTP status
- [Comparison](https://gortexa.sheng.page/en/comparison/): how this differs from running grpc-gateway alone
