Skip to main content
In Copper, transactions travel through context.Context. When a transaction is active, every Get, Select, and Exec that receives that context automatically runs inside it. Your queries and services do not need a tx parameter, and the same code works identically inside and outside a transaction.

Automatic Transactions per Request

Apps scaffolded with a database include csql.TxMiddleware as a global middleware. It wraps every mutating request (POST, PUT, PATCH, DELETE) in a transaction:
  • On a 2xx or 3xx response, the transaction commits.
  • On a 4xx or 5xx response, it rolls back.
  • GET, HEAD, and OPTIONS requests are not wrapped; reads auto-commit against the pool.
As a result, a handler that writes three rows and then fails is atomic by default, without any transaction plumbing in your handlers.
If the commit itself fails on a 2xx response, the middleware will respond with a 500 instead, so a client never sees a success status for data that was not committed.

Explicit Transactions

Outside of HTTP handlers, such as background workers or CLI tasks, you may manage transactions explicitly using InTx:
Return nil to commit, or an error to roll everything back. Panics are caught by a deferred rollback, so a transaction is never left open. InReadOnlyTx works the same way but always rolls back, which is useful for running multiple reads against one consistent snapshot.

Running After the Commit

Some side effects, such as publishing an event or sending an email, should only happen if the transaction commits. You may register them using OnCommit:
If the transaction rolls back, the callback is discarded. Since callbacks run in the background with a 30-second timeout, you should snapshot the data you need (like event above) before registering the callback rather than re-reading it inside.
If there is no transaction in the context, OnCommit runs the callback immediately in the background, so the same service code works with or without an ambient transaction.

Escaping the Transaction

Occasionally a write should persist even if the surrounding transaction rolls back, such as an audit log or a failure record. You may use csql.CtxWithoutTx to run a query outside the ambient transaction:
All other context values, such as deadlines, tracing, and auth, are preserved; only the transaction is removed.

Dry Runs

Because the request transaction commits or rolls back based on your response, you get dry-run support almost for free: do the real work, then roll back before responding.
The full code path runs, including validations, queries, and computed results, but nothing is persisted, and any OnCommit callbacks are discarded with the rollback.

Next

Next, learn how to define and evolve your schema with Migrations.