| outline |
|
||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| title | Proxy Options | ||||||||||||||||||||||||||||
| titleTemplate | NpgsqlRest | ||||||||||||||||||||||||||||
| description | Configure reverse proxy for NpgsqlRest. Forward requests to upstream services, cache responses in PostgreSQL, transform API responses with SQL functions. | ||||||||||||||||||||||||||||
| head |
|
Reverse proxy configuration for NpgsqlRest endpoints. When an endpoint is marked as a proxy, incoming HTTP requests are forwarded to an upstream service, and the response can either be returned directly to the client (passthrough mode) or processed by the PostgreSQL function (transform mode).
{
"NpgsqlRest": {
"ProxyOptions": {
"Enabled": false,
"Host": null,
"DefaultTimeout": "00:00:30",
"ForwardHeaders": true,
"ExcludeHeaders": ["Host", "Content-Length", "Transfer-Encoding"],
"ForwardResponseHeaders": true,
"ExcludeResponseHeaders": ["Transfer-Encoding", "Content-Length"],
"ResponseStatusCodeParameter": "_proxy_status_code",
"ResponseBodyParameter": "_proxy_body",
"ResponseHeadersParameter": "_proxy_headers",
"ResponseContentTypeParameter": "_proxy_content_type",
"ResponseSuccessParameter": "_proxy_success",
"ResponseErrorMessageParameter": "_proxy_error_message",
"ForwardUploadContent": false,
"MaxForwardedQueryParamLength": 2048
}
}
}| Setting | Type | Default | Description |
|---|---|---|---|
Enabled |
bool | false |
Enable proxy functionality for endpoints with proxy annotations. |
Host |
string | null |
Default upstream host URL. Used when the annotation has no URL. Ignored when the annotation specifies its own URL (absolute or relative). See URL Resolution. |
DefaultTimeout |
string | "00:00:30" |
Default timeout for proxy requests. Format: "HH:MM:SS" or interval format (e.g., "30s"). |
ForwardHeaders |
bool | true |
Forward request headers to upstream service. |
ExcludeHeaders |
array | ["Host", "Content-Length", "Transfer-Encoding"] |
Request headers to exclude from forwarding. |
ForwardResponseHeaders |
bool | true |
Forward response headers from upstream to client. |
ExcludeResponseHeaders |
array | ["Transfer-Encoding", "Content-Length"] |
Response headers to exclude from forwarding. |
ForwardUploadContent |
bool | false |
Forward raw multipart/form-data to upstream instead of processing locally. |
MaxForwardedQueryParamLength |
int | 2048 |
Maximum length (characters) of a single automatic parameter value appended to the proxy upstream query string. Server-filled values longer than this are skipped with a warning instead of producing an unusable request line (HTTP 414/431). 0 or less disables the guard. See Query-string length guard. |
These settings configure which parameter names receive proxy response data:
| Setting | Type | Default | Description |
|---|---|---|---|
ResponseStatusCodeParameter |
string | "_proxy_status_code" |
Parameter name for HTTP status code from upstream. |
ResponseBodyParameter |
string | "_proxy_body" |
Parameter name for response body content. |
ResponseHeadersParameter |
string | "_proxy_headers" |
Parameter name for response headers as JSON. |
ResponseContentTypeParameter |
string | "_proxy_content_type" |
Parameter name for Content-Type header value. |
ResponseSuccessParameter |
string | "_proxy_success" |
Parameter name for success indicator (true for 2xx status). |
ResponseErrorMessageParameter |
string | "_proxy_error_message" |
Parameter name for error message if request failed. |
When the PostgreSQL function has no proxy response parameters, the upstream response is returned directly to the client without opening a database connection:
create function get_external_data()
returns void
language sql
begin atomic;
select;
end;
comment on function get_external_data() is 'HTTP GET
@proxy https://api.example.com/data';Equivalent as a SQL file endpoint (sql/get-external-data.sql):
-- HTTP GET
-- @proxy https://api.example.com/data
select;When the PostgreSQL function has parameters matching the configured response parameter names, the proxy response is passed to the function for processing:
create function get_and_transform(
_proxy_status_code int default null,
_proxy_body text default null,
_proxy_headers json default null,
_proxy_content_type text default null,
_proxy_success boolean default null,
_proxy_error_message text default null
)
returns json
language plpgsql as $$
begin
if not _proxy_success then
return json_build_object('error', _proxy_error_message);
end if;
return json_build_object(
'status', _proxy_status_code,
'data', _proxy_body::json
);
end;
$$;
comment on function get_and_transform(int, text, json, text, boolean, text) is 'HTTP GET
@proxy https://api.example.com/data';When the PostgreSQL function has parameters matching these names (the defaults below, or whatever you set in the Response Parameter Names settings above), the upstream response data is bound to them after the request returns:
| Parameter Name | Type | Description |
|---|---|---|
_proxy_status_code |
int or text |
HTTP status code from upstream (e.g., 200, 404). Bound as text if the parameter is declared text/varchar, otherwise as an integer. |
_proxy_body |
text |
Response body content. |
_proxy_headers |
json |
Response headers as JSON object. |
_proxy_content_type |
text |
Content-Type header value. |
_proxy_success |
boolean |
True for 2xx status codes. |
_proxy_error_message |
text |
Error message if request failed. |
Parameters are matched by name (case-insensitive), not by position, and only the ones your function actually needs have to be declared. See How parameters are mapped in the annotation reference for the full rules.
Parameters that NpgsqlRest fills server-side (not supplied by the client) are forwarded to the upstream so the proxy receives the same parameter set the function would. All automatic sources are treated consistently:
- user claims (claim-mapped parameters,
user_params), - the IP address parameter,
- HTTP Custom Type fields (the auto-filled
responseBody/responseStatusCode/ … on a routine with an HTTP Custom Type parameter), - resolved-parameter expressions (values looked up server-side via SQL).
Where each automatic parameter is placed mirrors how the endpoint itself receives parameters — it is decided by RequestParamType, not by the HTTP method (a POST endpoint can use param_type query):
- The parameter designated as the body parameter (
@body_parameter_name) carries the raw request body. - Otherwise:
QueryString→ appended to the proxy query string;BodyJson→ merged into the proxy JSON body (typed: numbers, booleans, embedded JSON, or strings) when the proxy method can carry a body.
Forwarding is additive — the verbatim incoming request is still forwarded; the automatic parameters are added on top. Body merging applies only when the forwarded request carries a JSON content type (multipart / non-JSON is forwarded verbatim).
-- GET endpoint (QueryString): the auto-filled values are appended to the proxy query string.
create function proxy_with_claims(
_user_id text default null, -- Forwarded as ?userId=...
_user_name text default null, -- Forwarded as ?userName=...
_ip_address text default null, -- Forwarded as ?ipAddress=...
_user_claims json default null, -- Forwarded as ?userClaims=...
_proxy_status_code int default null,
_proxy_body text default null
)
returns json language plpgsql as $$
begin
return json_build_object('user', _user_id, 'data', _proxy_body);
end;
$$;
comment on function proxy_with_claims(text, text, text, json, int, text) is 'HTTP GET
@authorize
@user_params
@proxy https://api.example.com/data';::: tip Behavior note (3.18.1)
Before 3.18.1, user-claim and IP parameters were always forwarded on the query string, and HTTP Custom Type fields / resolved parameters were not forwarded at all. They are now unified under the rule above. For QueryString endpoints (the default for GET) the result is unchanged — values stay on the query string; for BodyJson endpoints the automatic parameters are now merged into the JSON body instead.
:::
When an automatic parameter is placed on the proxy query string (a QueryString endpoint — see above), an oversized value would be percent-encoded into the request line and produce a URL the upstream rejects (HTTP 414 URI Too Long / 431 Request Header Fields Too Large) or that resets the connection. A common trigger is an HTTP Custom Type field whose body holds a large payload (e.g. a scraped HTML page).
MaxForwardedQueryParamLength (default 2048) caps this. A single server-filled value longer than the limit is skipped with a warning rather than appended to the query string — the rest of the request still forwards normally. Set it to 0 (or less) to disable the guard entirely.
To forward a large value to the upstream, move it into the request body instead of the query string:
- use a body-carrying proxy method (
POST/PUT/PATCH) so it travels in the request body, and - designate the field with
@body_parameter_nameto carry the raw body, while the remaining small fields stay on the query string under the length guard.
::: tip New in 3.18.2
MaxForwardedQueryParamLength was added in 3.18.2. Previously a large auto-filled value (such as an HTTP Custom Type body field) was percent-encoded into the upstream query string unconditionally.
:::
When user_context is enabled, user context values are forwarded as HTTP headers to the upstream proxy:
create function proxy_with_context(
_proxy_status_code int default null,
_proxy_body text default null
)
returns json language plpgsql as $$
begin
return json_build_object('status', _proxy_status_code);
end;
$$;
comment on function proxy_with_context(int, text) is 'HTTP GET
@authorize
@user_context
@proxy https://api.example.com/data';
-- Headers forwarded: request.user_id, request.user_name, request.user_roles
-- (configurable via ContextKeyClaimsMapping)For upload endpoints with proxy, configure whether to process uploads locally or forward raw multipart data:
{
"NpgsqlRest": {
"ProxyOptions": {
"ForwardUploadContent": false
}
}
}| Value | Description |
|---|---|
false (default) |
Uploads are processed locally; proxy receives parsed data. |
true |
Raw multipart/form-data is streamed directly to upstream (memory-efficient for large files). |
- Passthrough mode: No database connection opened when function has no proxy response parameters
- Transform mode: Process upstream response in PostgreSQL before returning to client
- User claims forwarding: Authenticated user claims passed as query parameters to upstream
- User context headers: User context values passed as HTTP headers to upstream
- Streaming uploads: Memory-efficient streaming for large file uploads when
ForwardUploadContentis enabled - Timeout handling: Configurable per-request timeout with proper 504 Gateway Timeout responses
- Header forwarding: Configurable request/response header forwarding with exclusion lists
When a proxy annotation includes a relative path (starting with /), the request is routed to another endpoint on the same NpgsqlRest server:
comment on function my_aggregator() is 'HTTP GET
@proxy POST /api/data-source';Self-referencing calls bypass the HTTP stack entirely — the endpoint handler is invoked directly in-process via InternalRequestHandler, with zero network overhead.
::: info URL Resolution
A relative path in the annotation always creates a self-referencing internal call. The global ProxyOptions.Host setting is not prepended to relative paths — it is only used when the annotation has no URL at all. See URL Resolution for the full priority table.
:::
Combine with the @internal annotation to create endpoints accessible only via proxy but not exposed as public HTTP routes:
-- Internal helper: not accessible from outside
comment on function get_cached_rates() is 'HTTP GET
@internal';
-- Public endpoint that proxies the internal one
comment on function convert_currency(numeric, text, text) is 'HTTP GET
@proxy GET /api/get-cached-rates';Direct HTTP call to /api/get-cached-rates returns 404, but the proxy call works.
Production configuration with proxy enabled:
{
"NpgsqlRest": {
"ProxyOptions": {
"Enabled": true,
"Host": "https://api.internal.example.com",
"DefaultTimeout": "00:00:30",
"ForwardHeaders": true,
"ExcludeHeaders": ["Host", "Content-Length", "Transfer-Encoding", "Authorization"],
"ForwardResponseHeaders": true,
"ExcludeResponseHeaders": ["Transfer-Encoding", "Content-Length"],
"ForwardUploadContent": false,
"MaxForwardedQueryParamLength": 2048
}
}
}- Proxy Endpoints Guide — the full walkthrough
- proxy annotation - Enable proxy for specific endpoints
- user_context annotation - Enable user context forwarding
- user_parameters annotation - Enable user parameters forwarding
- Comment Annotations Guide - How annotations work
- Configuration Guide - How configuration works
- HTTP Client - HTTP Types for external API calls from PostgreSQL functions
- NpgsqlRest Options - Configure general NpgsqlRest settings
- Authentication - Configure authentication methods