I am using gqlgen
, sqlx
and pgx
. And I'm trying to use a custom scalar for sqlx
's types.JSONText
.
I have this attributes jsonb
field in items table.
-- migrations/001_up.sql
CREATE TABLE IF NOT EXISTS items (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
quantity INT NOT NULL,
attributes JSONB
);
I have these model structs:
// graph/model/item.go
type Item struct {
ID string `json:"id,omitempty" db:"id,omitempty"`
Quantity int `json:"quantity" db:"quantity"`
Attributes *Attributes `json:"attributes,omitempty" db:"attributes,omitempty"`
}
type Attributes types.JSONText
I have this graphql schema:
// graph/schema.graphql
type Item {
id: ID!
quantity: Int!
attributes: Attributes
}
scalar Attributes
I can successfully inserted into database, but got error at retrieving.
| id | quantity | attributes |
|---------------|----------|------------------------------------|
| 031e1489-... | 100 | {"size": "medium", "color": "red"} |
This is the log I got from db query:
>> items.Db: &{
031e1489-02c9-46d3-924d-6a2edf1ca3ba // id
100 // quantity
0xc000430600 // attributes
}
I tried to marshal attributes scalar:
// graph/model/item.go
...
func (a *Attributes) MarshalGQL(w io.Writer) {
b, _ := json.Marshal(a)
w.Write(b)
}
// Unmarshal here
...
Add custom scalar type in gqlgen.yml
:
...
Attributes:
model:
- github.com/my-api/graph/model.Attributes
But I got the string instead of json:
{
"data": {
"item": {
"id": "031e1489-02c9-46d3-924d-6a2edf1ca3ba",
"quantity": 100,
"attributes": "eyJjb2xvciI6ICJyZWQifQ==",
}
}
}
The desired output is:
{
"data": {
"item": {
"id": "031e1489-02c9-46d3-924d-6a2edf1ca3ba",
"quantity": 100,
"attributes": {
"size": "medium",
"color": "red",
}
}
}
}
What I am doing wrong?
Here are my attempts:
If I removed pointer from Attributes in Item struct, gqlgen
throws error:
go generate ./...
go: finding module for package github.com/my-api/graph/generated
generating core failed: type.gotpl: template: type.gotpl:49:28: executing "type.gotpl" at <$type.Elem.GO>: nil pointer evaluating *config.TypeReference.GOexit status 1
graph/resolver.go:3: running "go": exit status 1
make: *** [Makefile:2: gengql] Error 1
This returns the desired result, but I don't know how to use it with real data:
func (a *Attributes) MarshalGQL(w io.Writer) {
raw := json.RawMessage(`{"foo":"bar"}`)
j, _ := json.Marshal(&raw)
s := string(j)
w.Write([]byte(s))
}
Query result:
{
"data": {
"item": {
"id": "031e1489-02c9-46d3-924d-6a2edf1ca3ba",
"quantity": 100,
"attributes": {
"foo": "bar"
}
}
}
}
Attributes
is defined using types.JSONText
which is defined using json.RawMessage
which is defined using []byte
. This means that the underlying type of all 4 types, including []byte
, is []byte
, which in turn means that all of the 4 types can be converted to []byte
.
Hence it should be enough to do this:
func (a *Attributes) MarshalGQL(w io.Writer) {
w.Write([]byte(*a))
}