Skip to content

Postgres — vyuh_server_plugin_postgres

The reference relational DbPlugin. PostgresDbPlugin wraps the official postgres Dart driver behind the framework's DbAdapter boundary and publishes it as vyuh.db.

What It Provides

  • PostgresDbPlugin — claims the DbPlugin singleton role.
  • PostgresDbAdapter — implements open, close, execute, runTx, applyDdl, listen, rpc, and from(...).
  • PostgresDbSession — the transactional session passed to runTx.
  • PostgresConnectionConfig — typed env config consumed by the default plugin constructor.

Install

yaml
dependencies:
  vyuh_server_plugin_postgres:
    hosted: https://pub.vyuh.tech
    version: ^0.2.0

Env-Driven Wiring

The default constructor is lazy and env-driven. Register PostgresConnectionConfig on EnvPlugin, then add PostgresDbPlugin().

dart
import 'package:vyuh_server/vyuh_server.dart';
import 'package:vyuh_server_plugin_postgres/vyuh_server_plugin_postgres.dart';

final runtime = await VyuhServer.bootstrap(
  name: 'inventory-api',
  plugins: [
    EnvPlugin(
      configs: [
        TypedEnvConfig.factory(PostgresConnectionConfig.new),
      ],
    ),
    PostgresDbPlugin(),
  ],
);

PostgresConnectionConfig reads POSTGRES_URL from the framework env cascade. When it is absent, local development falls back to:

text
postgresql://postgres:postgres@127.0.0.1:5432/postgres?sslmode=disable

Production should set POSTGRES_URL, usually to the pooled Supabase connection URL. The plugin opens the adapter during init() and closes it during graceful shutdown.

Caller-Owned Connection

For tests or hosts that own the connection lifecycle, use PostgresDbPlugin.from:

dart
import 'package:postgres/postgres.dart';

final conn = await Connection.open(endpoint);

final runtime = await VyuhServer.bootstrap(
  name: 'inventory-test',
  plugins: [
    PostgresDbPlugin.from(connection: conn),
  ],
);

The plugin will not close a caller-owned connection.

Connection Settings

Pass settings: when you need to override the URL-derived ConnectionSettings:

dart
PostgresDbPlugin(
  settings: ConnectionSettings(
    sslMode: SslMode.require,
    connectTimeout: Duration(seconds: 10),
  ),
);

Using vyuh.db

Ad-Hoc Reads

dart
final rows = await vyuh.db.execute(
  'SELECT * FROM products WHERE tenant_id = @tenant LIMIT @limit',
  parameters: {
    'tenant': tenantId,
    'limit': 50,
  },
);

return jsonResponse(body: {'success': true, 'data': rows.asMaps});

Parameters are named and parameterized. Do not interpolate user input into SQL strings.

Transactions

dart
await vyuh.db.runTx((session) async {
  await session.execute(
    'INSERT INTO orders (id, tenant_id, total_cents) '
    'VALUES (@id, @tenant, @total)',
    parameters: {
      'id': orderId,
      'tenant': tenantId,
      'total': totalCents,
    },
  );

  for (final item in items) {
    await session.execute(
      'INSERT INTO order_items (order_id, sku, qty) '
      'VALUES (@order, @sku, @qty)',
      parameters: {
        'order': orderId,
        'sku': item.sku,
        'qty': item.qty,
      },
    );
  }
});

Throwing inside runTx rolls back. Returning normally commits.

DDL

dart
await vyuh.db.applyDdl([
  '''
  CREATE TABLE IF NOT EXISTS products (
    id           UUID PRIMARY KEY,
    tenant_id    TEXT NOT NULL,
    name         TEXT NOT NULL,
    price_cents  INTEGER NOT NULL,
    created_at   TIMESTAMPTZ DEFAULT NOW(),
    updated_at   TIMESTAMPTZ DEFAULT NOW()
  )
  ''',
  'CREATE INDEX products_tenant_id ON products(tenant_id)',
  'CREATE INDEX products_name ON products(name)',
]);

Query Builder

Prefer the adapter-neutral builder for feature/plugin code that should survive backend swaps:

dart
final result = await vyuh.db
    .from('products', schema: 'public')
    .select()
    .eq('tenant_id', tenantId)
    .order('name')
    .limit(50)
    .read();

LISTEN / NOTIFY

dart
await for (final payload in vyuh.db.listen('order_events')) {
  final event = jsonDecode(payload) as Map<String, Object?>;
  await dispatchOrderEvent(event);
}

LISTEN <channel> starts when the first subscriber attaches and UNLISTEN runs when the last subscriber detaches.

Where to Go Next

  • Entity CRUD — descriptor-driven CRUD over vyuh.db
  • Query — compile cdx_query JSON into Postgres SQL
  • vyuh.db — runtime accessor reference