Spoiler for Solution

Using custom struct for JSONB Type in SQLC

Muhammad Wyndham Haryata Permana
Gravel Product & Tech
2 min readAug 10, 2023

--

Before reading this content, I highly recommend you follow the Using SQLC for ORM alternative as prior reading material. This note will continues where we left of that part.

How SQLC Handles JSONB Types By Default

It’s common for any models to have metadata column that may contains any form of data that may related to our models but not classifiable:

CREATE TABLE products (
id BIGSERIAL PRIMARY KEY,
name varchar NOT NULL,
description TEXT,
slug varchar NOT NULL,
public_metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
private_metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
);

In this example we have public_metadata and private_metadata. By default, sqlc will assign those jsonb to a byte array:

type Product struct {
ID int64
Name string
Description pgtype.Text
Slug string
PublicMetadata []byte
PrivateMetadata []byte
}

This might be alright, but sometimes we want to at least have some structure to it. For example, we know that the metadata format will always be consist of type map[string]string or even slightly more abstract map[string]interface{}.

Only having []byte will requires us to cast and convert the format.

How to Add Type definition for JSONB

To solve that issue, we can do following things. First we may define our new type that we want that value to be assigned on:

package typeext

// in this example I want to assign it to map[string]interface{}
type JSONB map[string]interface{}

// You can put any kind of type tho, including struct
type CustomParams struct{
Params1 string
Params2 int64
}

Then we can just put it in the sqlc.yaml inside the gen: go: segment:

gen:
go:
package: "db"
sql_package: "pgx/v5"
out: "db"
overrides:
- column: "products.public_metadata"
go_type:
# for import, package and type please refer the go code above
import: "github.com/muhwyndhamhp/material-go/utils/typeext"
package: "typeext"
# put your type here,
# if you want struct instead, put the CustomParams
type: "JSONB"
- column: "products.private_metadata"
go_type:
import: "github.com/muhwyndhamhp/material-go/utils/typeext"
package: "typeext"
type: "JSONB"

and re generate via:

sqlc generate

It will change the product struct into:

type Product struct {
ID int64
Name string
Description pgtype.Text
Slug string
PublicMetadata typeext.JSONB
PrivateMetadata typeext.JSONB
}

And that’s it!

By the way, this solution does not only constrained on JSONB, but basically any data type, though it is most useful for JSONB type.

Thanks for reading!!

--

--