.../articles/
Golang: xo + sqlx

Golang: xo + sqlx

2019.04.30

When working with databases in Golang, it's common to map tables to structs.

However, since the database structure also needs to be updated over time as needed, manually keeping the code in sync with the current structure is surprisingly tedious (and, more than anything, error-prone).

So, let's try to simplify this process by using xo, which can generate code that matches the database, together with sqlx, which bills itself as an extension of the standard database/sql library.


We'll assume the following environment has already been set up.

PostgreSQL

Version 9.6, with the users table already created using the following DDL.

CREATE TABLE users (
    user_id bigserial PRIMARY KEY,
    email varchar(100) NOT NULL,
    created_at timestamp NOT NULL,
    updated_at timestamp NOT NULL
);

Golang

Golang version 1.12, with xo already installed.


About xo

Following the README, if you run the command below, files will be generated under models.

# generate code for a postgres schema
$ xo pgsql://user:pass@host/dbname -o models

The users table mentioned above is output as models/user.xo.go, as shown below.

// Package models contains the types for schema 'public'.
package models

// Code generated by xo. DO NOT EDIT.

import (
"errors"
"time"
)

// User represents a row from 'public.users'.
type User struct {
UserID    int64     `json:"user_id"`    // user_id
Email     string    `json:"email"`      // email
CreatedAt time.Time `json:"created_at"` // created_at
UpdatedAt time.Time `json:"updated_at"` // updated_at

// xo fields
_exists, _deleted bool
}

// omitted below

By updating the model definitions with xo alongside your database migrations, you can prevent the database structure and your code from drifting apart.

About sqlx

I'll leave the basic usage of sqlx to its README, but the following page has documentation on struct tags.

You can use the db struct tag to specify which column name maps to each struct field, or set a new default mapping with db.MapperFunc().

It doesn't appear to be strictly required, but it's preferable to explicitly specify columns with tags.


xo + sqlx

Since the structs output by xo only have a json tag, let's add a db tag as well so they work with sqlx.

To customize the output, as described in the README, you need to prepare a template file.

Once you copy the base templates into templates, you can edit the parts you need and use those templates.

# change to working project directory
$ cd $GOPATH/src/path/to/my/project

# create a template directory
$ mkdir -p templates

# copy xo templates for postgres
$ cp "$GOPATH/src/github.com/xo/xo/templates/*" templates/

# remove xo binary data
$ rm templates/*.go

This time, since we want to add a tag to the PostgreSQL output, we'll edit postgres.type.go.tpl.

We'll edit the part on line 10 where the tag is written, adding db:"{{ .Col.ColumnName }}" there.

- 	{{ .Name }} {{ retype .Type }} `json:"{{ .Col.ColumnName }}"` // {{ .Col.ColumnName }}
+	{{ .Name }} {{ retype .Type }} `json:"{{ .Col.ColumnName }}" db:"{{ .Col.ColumnName }}"` // {{ .Col.ColumnName }}

By specifying the template as an option, you can output a customized model definition.

$ xo pgsql://user:pass@localhost/dbname -o models --template-path templates/

With this, the resulting user.xo.go now looks like this, with db added to the tags.

// Package models contains the types for schema 'public'.
package models

// Code generated by xo. DO NOT EDIT.

import (
	"errors"
	"time"
)

// User represents a row from 'public.users'.
type User struct {
	UserID    int64     `json:"user_id" db:"user_id"`       // user_id
	Email     string    `json:"email" db:"email"`           // email
	CreatedAt time.Time `json:"created_at" db:"created_at"` // created_at
	UpdatedAt time.Time `json:"updated_at" db:"updated_at"` // updated_at

	// xo fields
	_exists, _deleted bool
}

// omitted below

With this, we've been able to make the model definitions generated by xo work with sqlx.

Next, I'd like to put together some notes on managing this alongside migrations.

written by

.../article/

Articles

All articles

From Firebase to Vercel, Contentful to microCMS — a migration log written with Claude Code

From Firebase to Vercel, Contentful to microCMS — a migration log written with Claude Code

We moved our corporate site's hosting and CMS, and made it bilingual along the way. The constraints we only found by running against real data were more useful than the migration itself, so this post focuses on where we got stuck.

Can't Read POST Data with Firebase Functions × Remix?

Can't Read POST Data with Firebase Functions × Remix?

How to read POST data from a Remix action when running on Firebase Functions.

Generative AI for Executives and Leaders: An Approach to Self-Driven DX

Generative AI for Executives and Leaders: An Approach to Self-Driven DX

Building a structure where executives and leaders themselves can identify issues and evaluate solutions using generative AI. We introduce how combining this with our hands-on support dramatically improves both the quality and speed of digital transformation.

Deploying a Monorepo Next.js App (App Router) to AWS Amplify

Deploying a Monorepo Next.js App (App Router) to AWS Amplify

Notes on the obstacles we hit while deploying a Next.js app managed in a monorepo to AWS Amplify.

Keeping Production Running Smoothly with Remote Work and Online Meetings [Documentation]

Keeping Production Running Smoothly with Remote Work and Online Meetings [Documentation]

Many production companies have adopted remote work as a result of the pandemic, and we are one of them.

Designing an E-Commerce Site That Sells: How to Find Great Reference Examples

Designing an E-Commerce Site That Sells: How to Find Great Reference Examples

There is no single formula for e-commerce design that sells. Driving revenue requires a solid concept, and getting to that concept requires thorough research.

Productivity Tools We Recommend as a Production Company, Including Services That Work Well Solo

Productivity Tools We Recommend as a Production Company, Including Services That Work Well Solo

With remote work becoming the norm during the COVID-19 pandemic, our team now works from home most days of the week.

We Released Thought Recorder, a Figma Plugin for Keeping a Commit History of Your Designs

We Released Thought Recorder, a Figma Plugin for Keeping a Commit History of Your Designs

We hope this helps web designers who work in Figma. Read on for how to use it.

How to Build an E-Commerce Site, and Which Platforms We Recommend

How to Build an E-Commerce Site, and Which Platforms We Recommend

Shopping online for fashion, appliances, and even groceries is now routine. With the pandemic accelerating the shift, we receive a steady stream of questions about which platform to use and how much it costs.

Generating FastAPI Schema Classes from OpenAPI

Generating FastAPI Schema Classes from OpenAPI

We chose FastAPI, a relatively modern framework, for a Python API project. FastAPI can generate an OpenAPI definition from your backend code, but here we do the opposite: generating FastAPI schema classes from an OpenAPI definition prepared in advance.

View all articles

Contact us