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, ),);GisilaOrmDatabaseProvider
Section titled “GisilaOrmDatabaseProvider”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), );}PostgresDbErrorMapper
Section titled “PostgresDbErrorMapper”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.