Skip to content
Written with Claude
IMPORTANT

As you may notice, this page and pretty much the entire website were obviously created with the help of AI. I wonder how you could tell? Was it a big "Written With Claude" badge on every page? I moved it to the top now (with the help of AI of course) to make it even more obvious. There are a few blogposts that were written by me manually, the old-fashioned way, I hope there will be more in the future, and those have a similar "Human Written" badge. This project (not the website), on the other hand, is a very, very different story. It took me more than two years of painstaking and unpaid work in my own free time. A story that, hopefully, I will tell someday. But meanwhile, what would you like me to do? To create a complex documentation website with a bunch of highly technical articles with the help of AI and fake it, to give you an illusion that I also did that manually? Like the half of itnernet is doing at this point? How does that makes any sense? Is that even fair to you? Or maybe to create this website manually, the old-fashioned way, just for you? While working a paid job for a salary, most of you wouldn't even get up in the morning. Would you like me to sing you a song while we're at it? For your personal entertainment? Seriously, get a grip. Do you find this information less valuable because of the way this website was created? I give my best to fix it to keep the information as accurate as possible, and I think it is very accurate at this point. If you find some mistakes, inaccurancies or problems, there is a comment section at the bottom of every page, which I also made with the help of the AI. And I woould very much appreciate if you leave your feedback there. Look, I'm just a guy who likes SQL, that's all. If you don't approve of how this website was constructed and the use of AI tools, I suggest closing this page and never wever coming back. And good riddance. And I would ban your access if I could know how. Thank you for your attention to this matter.

PROXY

Also known as

reverse_proxy (with or without @ prefix)

Mark endpoint as a reverse proxy that forwards requests to an upstream service.

Syntax

code
@proxy
@proxy [ host_url ]
@proxy [ http_method ]
@proxy [ http_method ] [ host_url ]

Description

The proxy annotation marks an endpoint as a reverse proxy. When a request arrives, NpgsqlRest forwards it to an upstream service and either returns the response directly (passthrough mode) or passes it to your PostgreSQL function for processing (transform mode).

Basic Usage

Passthrough Mode

For simple proxy forwarding without database processing:

sql
sql
create function get_external_data()
returns void
language sql
begin atomic;
select;
end;

comment on function get_external_data() is 'HTTP GET
@proxy';

When the function has no proxy response parameters, the upstream response is returned directly to the client without opening a database connection.

Transform Mode

To process the upstream response in PostgreSQL:

sql
sql
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';

Proxy Annotations

Basic Proxy with Default Host

Uses the host from ProxyOptions.Host configuration:

sql
sql
comment on function my_func() is '@proxy';

Proxy with Custom Host

Override the default host:

sql
sql
comment on function my_func() is '@proxy https://api.example.com';

Proxy with Custom HTTP Method

Override the upstream HTTP method (uses default host from configuration):

sql
sql
comment on function my_func() is '@proxy POST';

Combined Method and Host

Specify both HTTP method and host:

sql
sql
comment on function my_func() is '@proxy POST https://api.example.com';

Response Parameters

When the PostgreSQL function has parameters matching these names (configurable in ProxyOptions), the proxy response data is passed to the function:

Parameter NameTypeDescription
_proxy_status_codeintHTTP status code from upstream (e.g., 200, 404).
_proxy_bodytextResponse body content.
_proxy_headersjsonResponse headers as JSON object.
_proxy_content_typetextContent-Type header value.
_proxy_successbooleanTrue for 2xx status codes.
_proxy_error_messagetextError message if request failed.

Examples

API Gateway Pattern

Forward requests to different microservices:

sql
sql
-- Users service
create function users_api()
returns void
language sql
begin atomic;
select;
end;

comment on function users_api() is 'HTTP GET /api/users
@proxy https://users-service.internal:8080';

-- Orders service
create function orders_api()
returns void
language sql
begin atomic;
select;
end;

comment on function orders_api() is 'HTTP GET /api/orders
@proxy https://orders-service.internal:8080';

Data Enrichment

Fetch external data and enrich it with local data:

sql
sql
create function get_enriched_weather(
    city text,
    _proxy_status_code int default null,
    _proxy_body text default null,
    _proxy_success boolean default null
)
returns json
language plpgsql as $$
declare
    local_data json;
begin
    -- Get local city preferences
    select json_build_object('favorite', is_favorite, 'notes', notes)
    into local_data
    from user_city_preferences
    where city_name = city;

    if not _proxy_success then
        return json_build_object('error', 'Weather API unavailable');
    end if;

    return json_build_object(
        'weather', _proxy_body::json,
        'local', coalesce(local_data, '{}'::json)
    );
end;
$$;

comment on function get_enriched_weather(text, int, text, boolean) is 'HTTP GET
@proxy https://api.weather.com/v1/current?city={city}';

Authenticated Proxy with User Context

Forward authenticated requests with user information:

sql
sql
create function secure_api_call(
    _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, 'data', _proxy_body);
end;
$$;

comment on function secure_api_call(int, text) is 'HTTP GET
@authorize
@user_context
@proxy https://secure-api.internal/data';

User context values are forwarded as HTTP headers to the upstream service.

Proxy with User Parameters

Forward user claims as query parameters:

sql
sql
create function proxy_with_user(
    _user_id text default null,
    _proxy_body text default null
)
returns json language plpgsql as $$
begin
    return _proxy_body::json;
end;
$$;

comment on function proxy_with_user(text, text) is 'HTTP GET
@authorize
@user_params
@proxy https://api.internal/user-data';

The _user_id parameter value is forwarded to the upstream as ?userId=....

Configuration

Enable proxy functionality in your configuration:

json
json
{
  "NpgsqlRest": {
    "ProxyOptions": {
      "Enabled": true,
      "Host": "https://api.example.com",
      "DefaultTimeout": "30 seconds"
    }
  }
}

See Proxy Options for complete configuration reference.

Comments