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