Skip to main content
theauth-go ships four built-in storage adapters (memory, Postgres, MySQL and, as a separate module, SQLite). See Capability interfaces for which adapter implements which capability. Memory, Postgres and MySQL implement the full Storage interface and are set as Config.Storage. SQLite implements CoreStorage plus optional capabilities and is set as Config.CoreStorage (see Use the SQLite Backend).

In-memory (storage/memory)

  • Zero external dependencies. No database process needed.
  • Not persistent. All data is lost when the process restarts.
  • Use for: local development, integration tests, quick demos.

Postgres (storage/postgres)

  • Production-ready. Persistent, concurrent, supports all theauth features.
  • Dependencies: pgx/v5. Generated SQL from sqlc.
  • Migrations: Embedded under storage/postgres/migrations/. They are append-only: columns and tables are added, never renamed destructively.

Running migrations

The store does not auto-migrate. Run migrations yourself at startup or via a CI step, using the package-level Migrate function (not a method on Store):
Or apply the SQL files directly:

The Storage interface

Both adapters satisfy theauth.Storage. The interface is the only surface a custom backend needs to implement. See Storage Interface Reference for the full contract.

Special rules

  • Adding a method to Storage is a breaking change. New persistence operations land behind optional interfaces (e.g., OAuthServerStorage) detected at runtime via type assertion.
  • The Postgres adapter’s migrations directory is append-only. A renamed column ships as a new migration that adds the new column and removes the old one in a later release. This protects rolling-restart deployments.

MySQL 8.x (storage/mysql) (v2.4)

  • Full parity with the postgres adapter. Satisfies both theauth.Storage and OAuthServerStorage. All dialect differences (e.g., UUID() instead of gen_random_uuid(), GET_LOCK instead of advisory locks) are handled internally.
  • Dependencies: go-sql-driver/mysql v1.x. Generated SQL from sqlc targeting MySQL 8.x dialect.
  • Use for: deployments on PlanetScale, AWS RDS MySQL, or any MySQL 8.x host.

Running migrations

Like the Postgres adapter, storage/mysql embeds its migrations and exposes a package-level Migrate function that applies them under a GET_LOCK advisory lock:
Or apply the SQL files under storage/mysql/migrations/ directly:

Contract testing

The storagetest suite can verify your MySQL instance satisfies the full theauth-go contract. To run the contract tests against a live MySQL server, set the environment variable before running tests:
Without THEAUTH_MYSQL_CONTRACT=1, the tests are skipped, so the suite is safe to include in a standard go test ./... run that does not have a MySQL instance available.

Current parity caveats

  • Advisory lock serialization (GET_LOCK) is weaker than Postgres serializable transactions. Avoid running concurrent schema migrations.
  • MySQL does not support partial unique indexes. The state='current' JWKS constraint is enforced by application logic rather than a database constraint; the clientauthcache path is safe, but do not run multiple instances writing JWKS keys simultaneously without external coordination.

Choosing a backend

Last modified on October 7, 2026