Skip to content

Latest commit

 

History

History
264 lines (197 loc) · 9.43 KB

File metadata and controls

264 lines (197 loc) · 9.43 KB
outline
2
3
title PARAM Annotation
titleTemplate NpgsqlRest
description Rename, retype, set defaults, and configure composite type parameters. Works on all endpoint types — functions, procedures, and SQL file endpoints.
head
meta
name content
keywords
npgsqlrest param annotation, rename parameter, retype parameter, default value, composite type parameter, sql file parameters, api parameter names
meta
property content
og:title
NpgsqlRest PARAM Annotation
meta
property content
og:description
Rename, retype, set defaults, and configure composite type parameters.
meta
property content
og:type
article

PARAM

::: info Also known as param, parameter (with or without @ prefix) :::

Rename and optionally retype individual endpoint parameters. This provides better API ergonomics by replacing positional parameter names ($1, $2) or internal parameter names (_old_name) with cleaner, user-facing names.

Works on all endpoint types — functions, procedures, and SQL file endpoints.

::: tip The @param keyword is shared with the PARAMETER_HASH annotation (@param X is hash of Y). Both forms coexist without ambiguity — the parser distinguishes them by the presence of hash of in the annotation. :::

Syntax

@param <old_name> <new_name>
@param <old_name> <new_name> <type>
@param <old_name> is <new_name>
@param <old_name> is <new_name> <type>
@param <old_name> default <value>
@param <old_name> <new_name> default <value>
@param <old_name> <new_name> <type> default <value>
@param <old_name> is <new_name> default <value>
@param <old_name> is <new_name> <type> default <value>

# `=` can be used instead of `default` in all forms above:
@param <old_name> = <value>
@param <old_name> <new_name> = <value>
@param <old_name> <new_name> <type> = <value>
@param <old_name> is <new_name> = <value>
@param <old_name> is <new_name> <type> = <value>
  • old_name: The original parameter name (e.g., $1, $2, or _old_name)
  • new_name: The new parameter name for the HTTP API. Used as-is — no name conversion is applied. If you write @param $1 authorId, the HTTP parameter name is exactly authorId, not author_id or author-id.
  • type: Optional PostgreSQL type override (e.g., integer, text, boolean)

Both @param and @parameter (long form) are supported.

Examples

Rename Positional Parameters (SQL Files)

SQL files use PostgreSQL positional parameters ($1, $2, ...) which aren't user-friendly as HTTP parameter names. Use @param to give them meaningful names:

-- sql/get_reports.sql
-- HTTP GET
-- @param $1 from_date
-- @param $2 to_date
select id, title, created_at
from reports
where created_at between $1 and $2;

Without rename: GET /api/get-reports?$1=2024-01-01&$2=2024-12-31

With rename: GET /api/get-reports?from_date=2024-01-01&to_date=2024-12-31

Rename with Type Override

When parameter types can't be inferred correctly, or when you need to override the inferred type:

-- @param $1 user_id integer
-- @param $2 active boolean
select * from users where id = $1 and active = $2;

Rename Function Parameters

Works on function and procedure parameters too — useful when internal naming conventions (like _ prefixes) shouldn't leak into the API:

create function get_user_profile(_user_id int, _include_stats boolean)
returns json
language sql
begin atomic;
select json_build_object('id', id, 'name', name) from users where id = _user_id;
end;

comment on function get_user_profile(int, boolean) is '
HTTP GET
@param _user_id user_id
@param _include_stats include_stats
';

Without rename: GET /api/get-user-profile?_user_id=1&_include_stats=true

With rename: GET /api/get-user-profile?user_id=1&include_stats=true

"is" Style Syntax

The is keyword is optional and provides consistency with the existing @param X is hash of Y style:

-- These are equivalent:
-- @param $1 user_id
-- @param $1 is user_id

-- With type override:
-- @param $1 user_id integer
-- @param $1 is user_id integer

Claim Mapping with Renamed Parameters

Renamed parameters work with user_parameters claim mapping. When you rename a positional parameter to a claim-mapped name (like _user_id or _user_name), the parameter is automatically filled from the authenticated user's claims — just like it would be for a native function parameter.

-- sql/get_my_profile.sql
-- @authorize
-- @user_parameters
-- @param $1 _user_id
-- @param $2 _user_name
select $1 as user_id, $2 as user_name;

GET /api/get-my-profile (authenticated as user123) → [{"userId": "user123", "userName": "user"}]

The parameters are auto-filled from claims — the client doesn't need to send them. This is especially useful for SQL file endpoints where positional parameters ($1, $2) have no inherent name for claim matching.

Default Values (SQL File Parameters)

SQL file parameters can have default values via @param. When a parameter with a default is not provided in the request, the default value is bound instead of returning 404.

This is essential for SQL files because positional parameters ($1, $2) must always be bound — unlike PostgreSQL functions where the engine applies its own defaults.

Syntax

-- Separate annotations (rename first, then set default):
-- @param $1 user_id
-- @param user_id default null

-- Combined rename + default on a single line:
-- @param $1 user_id default null

-- Default without rename:
-- @param $1 default 'fallback'

-- Various value types:
-- @param $1 status default 'active'     -- text (single-quoted)
-- @param $1 amount default 42           -- number
-- @param $1 enabled default true        -- boolean
-- @param $1 filter default null         -- SQL NULL (unquoted)
-- @param $1 tag default 'null'          -- literal text "null" (quoted)
-- @param $1 val default                 -- no value = NULL

Inline comments after the value are ignored. The parser consumes only the value token (or quoted string) and stops.

-- `=` can be used instead of `default` in all forms:
-- @param $1 user_id = null
-- @param $1 user_id integer = 42
-- @param $1 is greeting = 'hey'
-- @param my_name = 'hello'

Value Parsing Rules (SQL Conventions)

  • Unquoted null (case-insensitive) → DBNull.Value
  • Single-quoted 'text value' → string literal (supports multi-word)
  • Unquoted value → raw string (Npgsql handles type conversion via NpgsqlDbType)

Example

User identity endpoint with claim-filled parameters that fall back to NULL:

/* HTTP GET
@authorize
@user_parameters
@param $1 _user_id default null
@param $2 _username default null
@param $3 _email default null
*/
select $1 as user_id, $2 as username, $3 as email;

When authenticated, claims fill the parameters automatically. The defaults ensure the parameters are always bindable.

Effects on Generated Output

  • TsClient: Parameters with defaults get ? suffix in TypeScript interfaces (optional)
  • OpenAPI: Parameters with defaults are marked required: false

Rename Validation

Parameter names are validated when renaming. Invalid renames are rejected with a warning log instead of silently creating broken endpoints.

Rules:

  • Must be a valid PostgreSQL identifier: starts with letter or _, followed by letters, digits, _, or $
  • Positional parameters ($1, $2) are allowed
-- Valid:
-- @param $1 user_id        ✓
-- @param $1 _val$1         ✓

-- Rejected (with warning log):
-- @param $1 1bad           ✗ starts with digit
-- @param $1 my-param       ✗ invalid character (hyphen)

Composite Type Parameters (SQL Files)

When a parameter type is a known composite type, the parameter is treated as a single text value. The SQL is never rewritten — it stays exactly as written.

HTTP custom types (auto-filled from HTTP calls):

-- @param $1 _response example_9.exchange_rate_api
select ($1::example_9.exchange_rate_api).body;

The framework makes the HTTP call and passes the response as a composite text value automatically.

Client-sent composite types:

-- @param $1 data my_composite_type
select ($1::my_composite_type).field1, ($1::my_composite_type).field2;

The client sends the value as PostgreSQL composite text format: ?data=("val1","val2").

If the type in @param is not a recognized PostgreSQL type or composite type, a warning is logged and the parameter keeps its original type from Describe.

Behavior

  • The rename applies to HTTP parameter names only — the SQL still uses the original parameter name or positional reference
  • Type overrides affect how the HTTP parameter value is parsed and converted before being sent to PostgreSQL
  • For multi-command SQL files, parameter types are merged across all statements — use type override to resolve conflicts
  • The parameter must exist in the endpoint's parameter list; otherwise a warning is logged and the rename is skipped
  • Renamed parameters participate in claim mapping — renaming $1 to _user_id enables automatic user_parameters filling from the name_identifier claim

Related