A better ORM for Go, based on non-empty interfaces and code generation.
Go Makefile PowerShell
Clone or download
Latest commit 91ede32 Jul 23, 2018
Failed to load latest commit information.
.github Update contributing notes. Jun 20, 2018
dialects Prepare v1.3.0 release. Dec 1, 2017
docs Set theme jekyll-theme-architect Feb 24, 2017
internal Minor tweak for SQLite3 schema. Dec 1, 2017
parse Parse all field types, not just for PK. Apr 22, 2017
reform-db Tweak reform-db flags and messages. Dec 6, 2017
reform Require Go 1.8+. Jun 16, 2018
.codecov.yml Set codecov coverage range. Dec 5, 2017
.gitignore Rework SET IDENTITY_INSERT for Go 1.10 Jun 21, 2018
.travis.yml Update generated file part header. Jun 20, 2018
CHANGELOG.md Update changelog. Jul 22, 2018
LICENSE Bump copyright year. Jan 9, 2017
Makefile Minor refactoring. Jun 21, 2018
README.md Add note about `mssql` driver. Jun 16, 2018
appveyor.yml Solve Windows-related issues from #136 (#140) Dec 29, 2017
base.go Add more tests for transactions. Dec 30, 2016
base_test.go Minor refactoring. Jun 21, 2018
db.go Prepare v1.3.0 release. Dec 1, 2017
db_test.go Minor refactoring. Jun 21, 2018
doc.go Require Go 1.8+. Jun 16, 2018
logger.go Minor style tweak. Feb 26, 2017
querier.go Minor style tweak. Feb 26, 2017
querier_commands.go Clarify MySQL’s clientFoundRows. Nov 14, 2017
querier_commands_test.go Minor refactoring. Jun 21, 2018
querier_examples_test.go Update faker import path. Jun 16, 2018
querier_selects.go Clean-up error handling style. Feb 5, 2017
querier_selects_test.go Added field "GroupID" to model Person with a default value Jul 8, 2016
tx.go Prepare v1.3.0 release. Dec 1, 2017



Release GoDoc Gitter Travis CI Build Status AppVeyor Build status Coverage Report Go Report Card

Reform gopher logo

A better ORM for Go and database/sql.

It uses non-empty interfaces, code generation (go generate), and initialization-time reflection as opposed to interface{}, type system sidestepping, and runtime reflection. It will be kept simple.

Supported SQL dialects:

RDBMS Library and drivers Tested with
PostgreSQL github.com/lib/pq (postgres) All supported versions.
MySQL github.com/go-sql-driver/mysql (mysql) All supported versions.
SQLite3 github.com/mattn/go-sqlite3 (sqlite3)
Microsoft SQL Server github.com/denisenkom/go-mssqldb (mssql, sqlserver) Windows: SQL2008R2SP2, SQL2012SP1, SQL2014, SQL2016. Linux: microsoft/mssql-server-linux:latest Docker image.



  1. Make sure you are using Go 1.8+. Install or update reform package, reform and reform-db commands (see about versioning below):

    go get -u gopkg.in/reform.v1/...
  2. Use reform-db command to generate models for your existing database schema. For example:

    reform-db -db-driver=sqlite3 -db-source=example.sqlite3 init
  3. Update generated models or write your own – struct representing a table or view row. For example, store this in file person.go:

    //go:generate reform
    type Person struct {
    	ID        int32      `reform:"id,pk"`
    	Name      string     `reform:"name"`
    	Email     *string    `reform:"email"`
    	CreatedAt time.Time  `reform:"created_at"`
    	UpdatedAt *time.Time `reform:"updated_at"`

    Magic comment //reform:people links this model to people table or view in SQL database. The first value in field's reform tag is a column name. pk marks primary key. Use value - or omit tag completely to skip a field. Use pointers for nullable fields.

  4. Run reform [package or directory] or go generate [package or file]. This will create person_reform.go in the same package with type PersonTable and methods on Person.

  5. See documentation how to use it. Simple example:

    // Use reform.NewDB to create DB.
    // Save record (performs INSERT or UPDATE).
    person := &Person{
    	Name:  "Alexey Palazhchenko",
    	Email: pointer.ToString("alexey.palazhchenko@gmail.com"),
    if err := DB.Save(person); err != nil {
    // ID is filled by Save.
    person2, err := DB.FindByPrimaryKeyFrom(PersonTable, person.ID)
    if err != nil {
    // Delete record.
    if err = DB.Delete(person); err != nil {
    // Find records by IDs.
    persons, err := DB.FindAllFrom(PersonTable, "id", 1, 2)
    if err != nil {
    for _, p := range persons {


reform was born during summer 2014 out of frustrations with existing Go ORMs. All of them have a method Save(record interface{}) which can be used like this:

orm.Save(User{Name: "gopher"})
orm.Save(&User{Name: "gopher"})

Now you can say that last invocation is obviously invalid, and that it's not hard to make an ORM to accept both first and second versions. But there are two problems:

  1. Compiler can't check it. Method's signature in godoc will not tell us how to use it. We are essentially working against those tools by sidestepping type system.
  2. First version is still invalid, since one would expect Save() method to set record's primary key after INSERT, but this change will be lost due to passing by value.

First proprietary version of reform was used in production even before go generate announcement. This free and open-source version is the fourth milestone on the road to better and idiomatic API.

Versioning policy

We are following Semantic Versioning, using gopkg.in and filling a changelog.

We use branch v1-stable (default on Github) for v1 development and tags v1.Y.Z for releases. All v1 releases are SemVer-compatible, breaking changes will not be applied. Canonical import path is gopkg.in/reform.v1. go get -u gopkg.in/reform.v1/reform will install latest released version. To install not yet released v1 version one can do checkout manually while preserving import path:

cd $GOPATH/src/gopkg.in/reform.v1
git fetch
git checkout origin/v1-stable
go install -v gopkg.in/reform.v1/reform

Branch v2-unstable is used for v2 development. It doesn't have any releases yet, and no compatibility is guaranteed. Canonical import path is gopkg.in/reform.v2-unstable.

Additional packages

Caveats and limitations

  • There should be zero pk fields for Struct and exactly one pk field for Record. Composite primary keys are not supported (#114).
  • pk field can't be a pointer (== nil doesn't work).
  • Database row can't have a Go's zero value (0, empty string, etc.) in primary key column.


Code is covered by standard MIT-style license. Copyright (c) 2016-2017 Alexey Palazhchenko. See LICENSE for details. Note that generated code is covered by the terms of your choice.

The reform gopher was drawn by Natalya Glebova. Please use it only as reform logo. It is based on the original design by Renée French, released under Creative Commons Attribution 3.0 USA license.


See Contributing Guidelines.