| outline |
|
||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| title | Code Generation Configuration | ||||||||||||||||||||||||||||
| titleTemplate | NpgsqlRest | ||||||||||||||||||||||||||||
| description | Generate TypeScript/JavaScript client code from your PostgreSQL API. Auto-create type-safe API clients with full type definitions from your database schema. | ||||||||||||||||||||||||||||
| head |
|
Configuration for generating TypeScript/JavaScript client code for NpgsqlRest endpoints.
{
"NpgsqlRest": {
"ClientCodeGen": {
"Enabled": false,
"FilePath": null,
"FileOverwrite": true,
"IncludeHost": true,
"CustomHost": null,
"CommentHeader": "Simple",
"CommentHeaderIncludeComments": true,
"BySchema": true,
"IncludeStatusCode": true,
"CreateSeparateTypeFile": true,
"ExportTypes": false,
"ImportBaseUrlFrom": null,
"ImportParseQueryFrom": null,
"IncludeParseUrlParam": false,
"IncludeParseRequestParam": false,
"HeaderLines": ["// autogenerated at {0}", ""],
"SkipRoutineNames": [],
"SkipFunctionNames": [],
"SkipPaths": [],
"SkipSchemas": [],
"DefaultJsonType": "any",
"UseRoutineNameInsteadOfEndpoint": false,
"ExportUrls": false,
"SkipTypes": false,
"UniqueModels": false,
"XsrfTokenHeaderName": null,
"ExportEventSources": true,
"CustomImports": [],
"CustomHeaders": {},
"IncludeSchemaInNames": true,
"ErrorExpression": "await response.json()",
"ErrorType": "{status: number; title: string; detail?: string | null} | undefined",
"OmitAutomaticParameters": false
}
}
}| Setting | Type | Default | Description |
|---|---|---|---|
Enabled |
bool | false |
Enable client code generation. |
FilePath |
string | null |
Output file path. Use {0} for schema name when BySchema is true. null to skip. |
FileOverwrite |
bool | true |
Overwrite existing files. |
BySchema |
bool | true |
Create separate files per PostgreSQL schema. |
IncludeSchemaInNames |
bool | true |
Include schema name in generated type names to avoid collisions. |
| Setting | Type | Default | Description |
|---|---|---|---|
IncludeHost |
bool | true |
Include current host in URL prefix. |
CustomHost |
string | null |
Custom host prefix for URLs. |
| Setting | Type | Default | Description |
|---|---|---|---|
CommentHeader |
string | "Simple" |
Comment header style: "None", "Simple", or "Full". |
CommentHeaderIncludeComments |
bool | true |
Include routine comments in header. |
| Style | Description |
|---|---|
None |
No comment header. |
Simple |
Add routine name, parameters, and return values (default). |
Full |
Add entire routine code as comment header. |
| Setting | Type | Default | Description |
|---|---|---|---|
IncludeStatusCode |
bool | true |
Include status code in response: {status: response.status, response: model}. |
ErrorExpression |
string | "await response.json()" |
Expression to parse error responses. Only used when IncludeStatusCode is true. |
ErrorType |
string | (see below) | TypeScript type for error responses. Only used when IncludeStatusCode is true. |
Default ErrorType: "{status: number; title: string; detail?: string | null} | undefined"
These options allow customization of error handling in generated code. Void functions and procedures also return the error object when IncludeStatusCode is true.
| Setting | Type | Default | Description |
|---|---|---|---|
CreateSeparateTypeFile |
bool | true |
Create separate {name}Types.d.ts file for global types. |
ExportTypes |
bool | false |
Emit interfaces with the export keyword so they can be imported by other modules. When true and CreateSeparateTypeFile is true, the separate type file becomes an importable module {name}Types.ts (instead of an ambient {name}Types.d.ts) and the client file imports the named types from it. No effect when SkipTypes is true. |
DefaultJsonType |
string | "any" |
Default TypeScript type for JSON types. |
SkipTypes |
bool | false |
Skip type generation for pure JavaScript output (changes .ts to .js). |
UniqueModels |
bool | false |
Merge models with same fields/types into one (reduces generated models). |
OmitAutomaticParameters |
bool | false |
Omit server-filled parameters from the generated request interface, query string, and body. See OmitAutomaticParameters: true. |
| Setting | Type | Default | Description |
|---|---|---|---|
ImportBaseUrlFrom |
string | null |
Module to import baseUrl constant from. |
ImportParseQueryFrom |
string | null |
Module to import parseQuery function from. |
CustomImports |
array | [] |
Custom import statements (full expressions). |
| Setting | Type | Default | Description |
|---|---|---|---|
IncludeParseUrlParam |
bool | false |
Include parseUrl: (url: string) => string parameter. |
IncludeParseRequestParam |
bool | false |
Include parseRequest: (request: RequestInit) => RequestInit parameter. |
| Setting | Type | Default | Description |
|---|---|---|---|
SkipRoutineNames |
array | [] |
Routine names to skip (without schema). |
SkipFunctionNames |
array | [] |
Generated function names to skip (without schema). |
SkipPaths |
array | [] |
URL paths to skip. |
SkipSchemas |
array | [] |
Schema names to skip. |
| Setting | Type | Default | Description |
|---|---|---|---|
ExportUrls |
bool | false |
Export URLs as constants. |
ExportEventSources |
bool | true |
Export EventSource create functions for streaming events. |
UseRoutineNameInsteadOfEndpoint |
bool | false |
Use routine name instead of endpoint name for functions. |
| Setting | Type | Default | Description |
|---|---|---|---|
CustomHeaders |
object | {} |
Custom headers added to each request. |
XsrfTokenHeaderName |
string | null |
XSRF token header name for anti-forgery (used in upload FORM POSTs). |
| Setting | Type | Default | Description |
|---|---|---|---|
HeaderLines |
array | ["// autogenerated at {0}", ""] |
Header lines for generated files. {0} = timestamp. |
This section walks through what the generated TypeScript actually looks like for each setting that affects output shape. All examples below are taken verbatim from real projects.
For a PostgreSQL function like:
create function public.who_am_i(
_user_id text default null,
_username text default null,
_email text default null
) returns table(user_id text, username text, email text)
language sql security definer as $$
select $1, $2, $3;
$$;
comment on function public.who_am_i is 'HTTP GET
@authorize
@user_parameters';Equivalent as a SQL file endpoint (sql/who-am-i.sql):
/*
HTTP GET
@authorize
@user_parameters
@param $1 user_id text
@param $2 username text
@param $3 email text
*/
select $1 as user_id, $2 as username, $3 as email;The TypeScript client generator treats both sources identically — the same IWhoAmIRequest / IWhoAmIResponse shapes and whoAmI() function are produced regardless of whether the endpoint is a function or a SQL file.
The generated TypeScript client looks like this:
interface IWhoAmIRequest {
userId?: string | null;
username?: string | null;
email?: string | null;
}
interface IWhoAmIResponse {
userId: string | null;
username: string | null;
email: string | null;
}
export async function whoAmI(
request: IWhoAmIRequest
) : Promise<{
status: number,
response: IWhoAmIResponse,
error: {status: number; title: string; detail?: string | null} | undefined
}> {
const response = await fetch(baseUrl + "/api/who-am-i" + parseQuery(request), {
method: "GET"
});
return {
status: response.status,
response: response.ok ? await response.json() as IWhoAmIResponse : undefined!,
error: !response.ok && response.headers.get("content-length") !== "0"
? await response.json() as {status: number; title: string; detail?: string | null}
: undefined
};
}PostgreSQL parameter names (snake_case) are converted to camelCase. Optional parameters (those with DEFAULT) become ? properties. The IncludeStatusCode setting (default true) wraps every response in { status, response, error } — this is what makes error handling consistent across every call.
Set IncludeStatusCode: false to skip the wrapper:
export async function whoAmI(request: IWhoAmIRequest): Promise<IWhoAmIResponse> {
const response = await fetch(baseUrl + "/api/who-am-i" + parseQuery(request), {
method: "GET"
});
return await response.json() as IWhoAmIResponse;
}Errors throw or surface as runtime exceptions instead of being part of the return type. Use this if you have application-level error handling middleware.
::: tip New in 3.18.2
OmitAutomaticParameters was added in 3.18.2 (also available on the HTTP File and OpenAPI generators). Default is false, so generated output is unchanged unless you opt in.
:::
Some parameters are filled by the server, so a value passed from the client would simply be ignored — emitting them as settable request properties is misleading. When true, such a parameter is dropped from the generated request interface, the query string, and the body when it is automatic and optional. "Automatic" covers:
- HTTP Custom Type fields (e.g. an auto-filled
responseBody), - resolved-parameter expressions,
- upload-metadata parameters,
- and — on endpoints that use user parameters — IP-address and user-claim parameters.
For a function whose only client-settable parameter is query, with an HTTP Custom Type field responseBody filled server-side:
// OmitAutomaticParameters: false (default) — responseBody appears even though the server overrides it
interface ISearchRequest {
query?: string | null;
responseBody?: string | null;
}
// OmitAutomaticParameters: true — only the real input remains
interface ISearchRequest {
query?: string | null;
}When every parameter is automatic, the request shape collapses entirely — the generated function takes no request argument:
export async function ping(): Promise<{ status: number; response: IPingResponse; /* ... */ }> {
const response = await fetch(baseUrl + "/api/ping", { method: "GET" });
// ...
}When true (default), interfaces are emitted into a sibling .d.ts file:
src/api/userApi.ts ← functions
src/api/userApiTypes.d.ts ← interfaces (type-only)
The .d.ts file is pure type declarations:
//
// autogenerated file - do not edit
//
interface IWhoAmIRequest {
userId?: string | null;
username?: string | null;
email?: string | null;
}
interface IWhoAmIResponse {
user_id: string | null;
username: string | null;
email: string | null;
}Set CreateSeparateTypeFile: false to emit interfaces inline in the same file as the functions.
By default, interfaces are emitted as plain interface declarations. That makes them module-private when inlined (CreateSeparateTypeFile: false) and ambient/global when in the separate .d.ts file — in neither case can another module import them. Set ExportTypes: true to emit them as export interface so they can be imported.
Inline (CreateSeparateTypeFile: false) — interfaces and functions share one file, with the interfaces now exported:
export interface ISearchProductsRequest {
query?: string | null;
maxPrice?: number | null;
}
export interface ISearchProductsResponse {
id: number | null;
name: string | null;
price: number | null;
}
export async function searchProducts(
request: ISearchProductsRequest
) : Promise<ApiResult<ISearchProductsResponse[]>> {
// ...
}Separate file (CreateSeparateTypeFile: true) — the type file becomes an importable module {name}Types.ts (not an ambient {name}Types.d.ts), and the client file imports the named types from it:
src/api/searchProducts.ts ← functions + `import type { ... } from "./searchProductsTypes"`
src/api/searchProductsTypes.ts ← `export interface ...`
// searchProducts.ts
import type { ISearchProductsRequest, ISearchProductsResponse } from "./searchProductsTypes";
const baseUrl = "";
// ...ExportTypes has no effect when SkipTypes is true (no types are generated). Defaulting to false keeps existing output unchanged.
When enabled, a URL builder for each endpoint is exported:
export const cancelComputeUrl = () => baseUrl + "/api/cancel-compute";
export const computeVisualizationUrl = (request: IComputeVisualizationRequest) =>
baseUrl + "/api/compute-visualization" + parseQuery(request);Useful when you need to construct a URL but don't want to make the request immediately — for <a href> links, <form action> attributes, or passing to a third-party library.
For endpoints with the @sse annotation, an EventSource constructor is exported:
export const createComputeVisualizationEventSource = (id: string = "") =>
new EventSource(baseUrl + "/api/compute-visualization/info?" + id);The optional id parameter scopes the event stream to a specific execution. See the SSE annotation for usage.
By default, generated files include their own baseUrl constant and parseQuery helper. To share these across files, point them to a module that exports them:
Generated files now import instead of inlining:
//
// autogenerated file - do not edit
//
import { baseUrl } from "$lib/urls";
import { parseQuery } from "$lib/urls";Where $lib/urls.ts is a file you maintain:
export const baseUrl = import.meta.env.VITE_API_BASE_URL ?? "";
export const parseQuery = (query: Record<string, any>) => "?" + Object.keys(query ?? {})
.map(key => {
const value = query[key] ?? "";
if (Array.isArray(value)) {
return value.map(s => s ? `${key}=${encodeURIComponent(s)}` : `${key}=`).join("&");
}
return `${key}=${encodeURIComponent(value as string)}`;
})
.join("&");This is the recommended pattern for SvelteKit / Next.js / Vite apps where baseUrl should come from environment variables.
When endpoints use path parameters (e.g., @path /products/{p_id}), template literals are used in URLs:
export async function getProduct(request: { pId: number }) {
const response = await fetch(`${baseUrl}/products/${request.pId}`, {
method: "GET"
});
return { status: response.status, response: await response.json() };
}parseQuery is only emitted when at least one endpoint has actual query-string parameters. Endpoints with only path parameters skip the helper entirely.
By default, function names come from the URL path (kebab-case → camelCase): /api/who-am-i → whoAmI().
With UseRoutineNameInsteadOfEndpoint: true, function names come from the PostgreSQL routine name instead: public.who_am_i → whoAmI().
Useful when you customize URL paths via @path annotations but want function names that still match the SQL routine names. Combines well with IncludeSchemaInNames: false to drop schema prefixes from generated names.
Default behavior. With FilePath: "./src/api/{0}Api.ts", the {0} placeholder is replaced with each schema name:
src/api/publicApi.ts ← from public schema
src/api/publicApiTypes.d.ts
src/api/billingApi.ts ← from billing schema
src/api/billingApiTypes.d.ts
Set BySchema: false and use a fixed filename (no {0}) to emit a single combined file.
The simplest setup — one file per schema, everything else default:
{
"NpgsqlRest": {
"ClientCodeGen": {
"Enabled": true,
"FilePath": "./src/{0}Api.ts"
}
}
}This is what every example in the examples repository uses.
Use this when you don't want TypeScript:
{
"NpgsqlRest": {
"ClientCodeGen": {
"Enabled": true,
"FilePath": "./src/api/client.js",
"BySchema": false,
"SkipTypes": true,
"IncludeSchemaInNames": false
}
}
}SkipTypes: true removes all TypeScript syntax (interfaces, type annotations) so the file is valid JavaScript despite the .ts → .js extension.
Real-world configuration with shared baseUrl/parseQuery from a $lib alias, URL constants for use in templates, and routine-name-based function naming:
{
"NpgsqlRest": {
"ClientCodeGen": {
"Enabled": true,
"FilePath": "./src/app/api/{0}Api.ts",
"FileOverwrite": true,
"IncludeHost": true,
"CommentHeader": "Simple",
"CommentHeaderIncludeComments": true,
"BySchema": true,
"IncludeStatusCode": true,
"CreateSeparateTypeFile": true,
"ImportBaseUrlFrom": "$lib/urls",
"ImportParseQueryFrom": "$lib/urls",
"DefaultJsonType": "string",
"UseRoutineNameInsteadOfEndpoint": true,
"ExportUrls": true,
"ExportEventSources": true,
"IncludeSchemaInNames": false,
"HeaderLines": [
"//",
"// autogenerated file - do not edit",
"//"
]
}
}
}What this gives you:
- One
*Api.ts+ one*ApiTypes.d.tsfile per schema in./src/app/api/ - Generated files import
baseUrlandparseQueryfrom$lib/urls(your own module) JSONPostgreSQL columns typed asstringinstead ofany— explicit casting at the call site- Function names match SQL routine names (good for grep / refactoring across SQL and TS)
- URL builder constants exported (
computeUrl(),loginUrl(), etc.) for use in<a href>, forms, and library integrations EventSourcefactory functions for any@sseendpoints- Schema name dropped from interface names (
ICancelComputeRequest, notIMathmoduleCancelComputeRequest)
For projects that need to add custom headers to every request or import external utilities into the generated files:
{
"NpgsqlRest": {
"ClientCodeGen": {
"Enabled": true,
"FilePath": "./src/api/{0}Api.ts",
"ImportBaseUrlFrom": "@/config",
"ImportParseQueryFrom": "@/utils/query",
"CustomImports": [
"import { handleError } from '@/utils/errors';"
],
"CustomHeaders": {
"X-Client-Version": "\"1.0.0\"",
"X-Client-Platform": "\"web\""
},
"XsrfTokenHeaderName": "X-XSRF-TOKEN"
}
}
}Note the CustomHeaders value syntax — values are emitted as TypeScript expressions, so a literal string requires escaped quotes ("\"1.0.0\""). To use a dynamic value, write a JS expression: "() => localStorage.getItem('app-version')".
- TSCLIENT annotation - Per-endpoint TypeScript client control
- Comment Annotations Guide - How annotations work
- Configuration Guide - How configuration works
- HTTP Files - Configure HTTP file generation
- OpenAPI - Configure OpenAPI specification generation
- NpgsqlRest Options - Configure general NpgsqlRest settings
- TSCLIENT - Per-endpoint TypeScript client control
{ "ImportBaseUrlFrom": "$lib/urls", "ImportParseQueryFrom": "$lib/urls" }