Skip to content

Dart Code Generation

Configuration for generating Dart client code for NpgsqlRest endpoints, targeting Flutter projects. Available since version 3.20.0 (NpgsqlRest.DartClient plugin).

The generated code depends only on package:http and works on all Flutter targets (mobile, desktop, web).

Overview

json
json
{
  "NpgsqlRest": {
    "DartClientCodeGen": {
      "Enabled": false,
      "FilePath": null,
      "FileOverwrite": true,
      "IncludeHost": true,
      "CustomHost": null,
      "CommentHeader": "Simple",
      "CommentHeaderIncludeComments": true,
      "BySchema": true,
      "IncludeStatusCode": true,
      "SeparateModelsFile": false,
      "ImportBaseUrlFrom": null,
      "HeaderLines": ["// autogenerated at {0}", ""],
      "SkipRoutineNames": [],
      "SkipFunctionNames": [],
      "SkipPaths": [],
      "SkipSchemas": [],
      "DefaultJsonType": "dynamic",
      "UseRoutineNameInsteadOfEndpoint": false,
      "ExportUrls": false,
      "UniqueModels": false,
      "XsrfTokenHeaderName": null,
      "ExportEventSources": true,
      "IncludeParseUrlParam": false,
      "IncludeParseRequestParam": false,
      "CustomImports": [],
      "CustomHeaders": {},
      "IncludeSchemaInNames": true,
      "ErrorTypeName": "ApiError",
      "ResultTypeName": "ApiResult",
      "OmitAutomaticParameters": false,
      "ModelPrefix": null,
      "ModelSuffix": null,
      "UseDateTimeType": true
    }
  }
}

Set Enabled to true and point FilePath at your Flutter project, e.g. "../my_app/lib/api/{0}.dart"{0} is the PostgreSQL schema name (snake case) or the module name from the dartclient_module annotation.

Generated code

For every endpoint the generator emits:

  • A request class with a toJson() method — one field per routine parameter, all optional named constructor parameters. Parameters with PostgreSQL defaults are omitted from the JSON body and query string when null, so the database default applies.
  • A response class with a fromJson() factory — one field per column (or a plain Dart type for scalar responses). Composite type columns become nested model classes.
  • An async function wrapping the HTTP call — query string, path parameters and JSON body handled automatically.
dart
dart
// generated
Future<List<SearchItemsResponse>> searchItems(SearchItemsRequest request) async {
  final uri = Uri.parse('$baseUrl/api/search-items' + _query({
    'query': request.query,
    'page': request.page,
    if (request.limit != null) 'limit': request.limit,
  }));
  final response = await _send('GET', uri, ...);
  return (jsonDecode(utf8.decode(response.bodyBytes)) as List)
      .map((e) => SearchItemsResponse.fromJson(e as Map<String, dynamic>))
      .toList();
}

With IncludeStatusCode (or the dartclient_status_code annotation), responses are wrapped as ApiResult<T> with the HTTP status code and a problem-details ApiError:

dart
dart
final result = await searchItems(SearchItemsRequest(query: 'a', page: 1));
if (result.ok) {
  print(result.response);
} else {
  print('${result.status}: ${result.error?.title}');
}

Upload endpoints generate multipart functions (http.MultipartRequest, files sent as form field file, an optional progress callback reporting uploaded bytes, optional XSRF token header). Login endpoints return the token String; logout endpoints return void. proxy_out and void proxy pass-through endpoints return Future<http.Response> directly.

Server-sent events

SSE endpoints generate a create{Name}EventSource factory returning an SseSubscription (built on streamed package:http responses — no extra dependency) and an SSE-aware function:

dart
dart
final result = await sseEndpoint(
  SseEndpointRequest(message: 'hello'),
  onMessage: (message) => print('event: $message'),
);

The function opens the event stream with a generated execution id, waits awaitConnectionMs, sends the request with the execution-id header attached, and closes the stream closeAfterMs milliseconds after the response — the same lifecycle as the TypeScript client. Use the factory directly for long-lived subscriptions:

dart
dart
final events = await createSseEndpointEventSource((message) => print(message));
// ...
await events.close();

baseUrl and testing

Each generated module declares:

dart
dart
String baseUrl = '';        // set per flavor/environment at app startup

/// Override to inject a custom [http.Client] (e.g. MockClient in tests).
http.Client? httpClient;

In widget or unit tests, assign a MockClient from package:http/testing.dart to httpClient to intercept all requests.

Type mapping

PostgreSQLDart
smallint, integer, bigintint
real, double precision, numeric, moneydouble
booleanbool
json, jsonbdynamic (configurable via DefaultJsonType)
date, timestamp, timestamptzDateTime (configurable via UseDateTimeType)
everything else (text, uuid, time, interval, ranges, ...)String
arraysList<T>

All model fields are nullable (T?). snake_case names become lowerCamelCase fields with the raw wire name kept as the JSON key. Dart reserved words and dart:core type names are escaped with a trailing underscore (classclass_).

Comment annotations

Per-endpoint overrides in routine comments:

AnnotationDescription
dartclient = offExclude the endpoint from generation.
dartclient_module=nameGroup the endpoint into module file name. Falls back to tsclient_module, so SQL-file directory grouping applies to both generators.
dartclient_status_code=trueWrap this endpoint's response in ApiResult<T>.
dartclient_events=falseGenerate a plain function for an SSE endpoint (no event-source parameters).
dartclient_parse_url=true / dartclient_parse_request=trueAdd the per-call parseUrl/parseRequest rewrite hooks to this endpoint.
dartclient_export_url=trueAlso emit a {name}Url() URL-builder function.
dartclient_url_only=trueEmit only the URL-builder function.

Options

Options mirror the TypeScript code generation options where applicable. Dart-specific options:

SeparateModelsFile

  • Type: boolean
  • Default: false

Emit request/response (and composite) model classes into a separate {name}_models.dart file. The client file imports and re-exports the models file so consumers keep a single import.

ModelPrefix / ModelSuffix

  • Type: string | null
  • Default: null

Prefix/suffix for generated model class names, useful to avoid class-name collisions when importing several generated modules into one file.

UseDateTimeType

  • Type: boolean
  • Default: true

When true, date, timestamp and timestamptz values map to Dart DateTime (parsed with DateTime.parse, serialized with toIso8601String). When false, they map to String.

ErrorTypeName / ResultTypeName

  • Type: string
  • Default: "ApiError" / "ApiResult"

Names of the generated status-wrapper classes. Only used when IncludeStatusCode is true.

ExportEventSources

  • Type: boolean
  • Default: true

Emit public create{Name}EventSource factory functions for SSE endpoints. When false, the factories get a private (underscore) name and are only used internally — the Dart analog of not exporting them.

IncludeParseUrlParam / IncludeParseRequestParam

  • Type: boolean
  • Default: false

Add optional named parameters Uri Function(Uri uri)? parseUrl and http.Request Function(http.Request request)? parseRequest (http.MultipartRequest variant for uploads) that can rewrite the URI or request before it is sent. For intercepting every request globally, prefer assigning a custom httpClient.

Known limits

  • numeric/money map to double — arbitrary precision beyond IEEE 754 is lost. Dart int on Flutter Web is limited to 2^53.
  • Multi-dimensional arrays are typed as one-dimensional List<T> (PostgreSQL does not expose array dimensionality — same limitation as the TypeScript client).

Comments