Skip to content

gisila integration (web.dart)

package:gisila_orm/web.dart connects the ORM to the gisila web framework. It has two classes: a database provider that hands each request the shared Database, and an error mapper that turns Postgres errors into HTTP responses.

import 'package:gisila/gisila.dart';
import 'package:gisila_orm/gisila.dart';
import 'package:gisila_orm/web.dart';
final db = await Database.connect(await DatabaseConfig.fromEnvironment());
final orm = GisilaOrmDatabaseProvider(db);
final app = GisilaApp(
config: AppConfig(
database: orm,
dbErrorMapper: const PostgresDbErrorMapper(),
healthCheckPath: '/healthz',
healthCheck: orm.isHealthy,
),
);
class GisilaOrmDatabaseProvider extends DatabaseProvider<Database> {
GisilaOrmDatabaseProvider(Database database, {bool closeOnDispose = true});
}

A gisila DatabaseProvider whose per-request session is the shared Database. Handlers and services read it with ctx.db<Database>() (or db<Database>() inside a Service).

When to use: in every gisila app that uses gisila_orm. It doesn’t open a transaction per request; open one in your service code where you need it with db.transaction(...).

Member Type Default Description
database Database – The shared database.
closeOnDispose bool true Close the database’s pools when the app stops (app.stop()). Set false when something else owns the Database.
open(request) Future<Database> Returns database. Nothing is opened per request; the pool lends a connection per statement.
close(session, {ok}) Future<void> Does nothing.
dispose() Future<void> Closes database when closeOnDispose.
isHealthy() Future<bool> true when SELECT 1 succeeds, false on any error. Pass it as AppConfig.healthCheck.

Example: a service.

class AuthorService extends Service {
Future<List<Author>> list({int limit = 50}) =>
authors().orderBy(AuthorTable.name).limit(limit).all(db<Database>().context());
Future<Author> create(String name, String email) => db<Database>().transaction(
(tx) => authors().insert({'name': name, 'email': email}).one(tx),
);
}
class PostgresDbErrorMapper extends DbErrorMapper {
const PostgresDbErrorMapper();
HttpException? map(Object error, StackTrace stack);
}

Maps an uncaught PostgresException from a handler to a client error. Anything it doesn’t recognise (including non-Postgres errors) returns null, which leaves it to the framework as a 500.

When to use: always, so a duplicate email becomes a 409 instead of a 500. Still catch errors yourself when you want a domain-specific message.

Exception Status code Message
PostgresUniqueViolationException 409 unique_violation "email" is already taken. (or a generic message when no column is known)
PostgresExclusionViolationException 409 exclusion_violation This conflicts with an existing record.
PostgresForeignKeyViolationException 400 foreign_key_violation "author_id" must refer to an existing record.
PostgresNotNullViolationException 400 not_null_violation "title" is required.
PostgresCheckViolationException 400 check_violation A value is not allowed.
PostgresDataException and subclasses 400 invalid_value A value is invalid for its field.
PostgresConnectionException 503 database_unavailable The database is unavailable.

The column names go in details.fields (for example {"fields": ["email"]}). They are taken from the server’s column field, or parsed from Key (email)=(...) in the detail text. The value itself is never included, so the response can’t reveal which emails are registered.

The 503 row covers connection errors reported by the server (SQLSTATE class 08). A failure inside the driver, such as a refused TCP connection, isn’t translated into a PostgresException and stays a 500.

See also: Use with the gisila framework, Errors, gisila framework.