# Observers

`hookm` is a list of read-only observers. Omitted, no observer runs. Each method is optional. They run in list order. They are events only. They cannot change a query, a commit, or a rollback. An error a hook throws is dropped and never reaches the call. The next observer still runs.

| Method | When |
| --- | --- |
| `onNotice` | A server notice, such as `RAISE NOTICE`. |
| `onTransaction` | A transaction or batch starts, commits, or rolls back. |
| `onError` | An error the runtime could not give to a caller, such as one from an `afterCommit` callback. |

### onNotice

A statement's notices are passed to every `onNotice`.

| Field | Value |
| --- | --- |
| `severity` | A string. |
| `message` | A string. |
| `code` | Optional. A string. |

<Callout type="info" title="Bun.sql">
Bun.sql does not surface `RAISE NOTICE`; see [Drivers](/docs/define/connect/drivers).
</Callout>

### onTransaction

`onTransaction` hears that a transaction or savepoint changed phase. `phase` is `start`, `commit`, or `rollback`. `depth` is 0 for the transaction and 1 and up for savepoints.

```ts title="db.ts"
import { connect } from "okmodel/pg/postgresjs";
import { schema, table, t } from "okmodel/pg";

const notes = table("notes", {
  id: t.identity(),
  title: t.text(),
});

const app = schema({ tables: [notes] });

const seen: string[] = [];

export const db = connect(process.env.DATABASE_URL!, {
  schema: app,
  hookm: [
    {
      onTransaction: (event) => {
        seen.push(`${event.phase}:${event.depth}`); // [!code focus]
      },
    },
  ],
});
```

### onError

`error` is `unknown`. An `afterCommit` callback is one case. That error is reported to `onError` and never changes the commit result.

[OpenTelemetry](/docs/define/connect/otel) turns these observers, and `onRoute`, into spans.
