2021年3月10日 星期三

Gqlgen custom scalar and datetime two-way bind serialize

 使用 GraphQL 如果需要使用到一些自訂的型別,如同特殊的 DateTime Serialize 的狀況,可以使用 Custom Scalars,本篇文章說明如何使用 Go-gqlgen 的自訂純量 (scalars)。

預先防雷警告,以下文章只是說明純量的操作,本文章輸入出的結果只是作為範例,它必定會失敗,如果是在找 gqlgen 有沒有時間的型別,是有的。


在 GraphQL Schema 檔案開頭就使用:


scalar Time


就可以直接在各處定義中使用 Time 這個型別,例如:

createdAt: Time!


使用 gqlgen ,最好要使用 gqlgen.yml 設定檔去帶入 scalars 模組位置,差別就在於使用指令:

go run github.com/99designs/gqlgen generate

此指令時,會自動去讀取該指令所在目錄下的 gqlgen.yml 檔案。


在設定之前,先說明一下該 yaml 設定檔的內容:

# graphql schema 所有檔案的位置
schema:
    - graphql/schema/*.graphql

# generated.go 這個 interface 最後要產生在哪邊
exec:
    filename: graphql/schema/generated.go
    package: schema

# 給 Apollo federation 使用的,沒有用 apollo 就不需要打開這項設定
federation:
    filename: graphql/schema/federation.go
    package: schema

# models_gen.go 這個所有結構 struct 的檔案最後要產生在哪邊
model:
    filename: graphql/schema/models_gen.go
    package: schema

# 需不需要自動幫忙產生預設實作的 resolvers.go 檔案 (不要可以自己關閉)
# auto implements
resolver:
    layout: follow-schema
    dir: graphql/mar
    package: graph
    filename_template: "{name}.resolvers.go"

# Optional: turn on use ` + "`" + `gqlgen:"fieldName"` + "`" + ` tags in your models
# struct_tag: json

# Optional: turn on to use []Thing instead of []*Thing
# omit_slice_element_pointers: false

# Optional: set to speed up generation time by not performing a final validation pass.
# skip_validation: true

# gqlgen will search for any type names in the schema in these go packages
# if they match it will use them, otherwise it will generate them.
#autobind:
# - "github.com/your/app/graph/model"

# This section declares type mapping between the GraphQL and go type systems
#
# The first line in each type will be used as defaults for resolver arguments and
# modelgen, the others will be allowed when binding to fields. Configure them to
# your liking
# 額外 (第三方以上) 的純量模組使用
models:
    ID:
        model:
            - github.com/99designs/gqlgen/graphql.ID
            - github.com/99designs/gqlgen/graphql.Int
            - github.com/99designs/gqlgen/graphql.Int64
            - github.com/99designs/gqlgen/graphql.Int32
    Int:
        model:
            - github.com/99designs/gqlgen/graphql.Int
            - github.com/99designs/gqlgen/graphql.Int64
            - github.com/99designs/gqlgen/graphql.Int32


對於自訂純量,如果要在 graphql 檔案中,使用自訂型別 DateTime,則要先開一個 go 的檔案,使用以下設定做為參考:

package scalars

import (
	"fmt"
	"io"
	"time"
)

// 定義一個型別,叫做 DateTime (有大小寫敏感)
type DateTime struct {
	t time.Time
}

func New(t time.Time) *DateTime {
	return &DateTime{
		t: t,
	}
}

func (d *DateTime) GetTime() time.Time {
	return d.t
}

// gqlgen 會在每一次 query 中自動做反序列化
func (d *DateTime) UnmarshalGQL(vi interface{}) (err error) {
	v, ok := vi.(string)
	if !ok {
		return fmt.Errorf("unknown type of DateTime: `%+v`", vi)
	}
	if d.t, err = time.Parse("2006-01-02T15:04:05.000Z", v); err != nil {
return err } return nil } // gqlgen 會在 query 要回傳 response query 時做序列化,把日期換成時間再丟出去 func (d DateTime) MarshalGQL(w io.Writer) { w.Write([]byte(d.t.Format(TimeLayout))) }

假設這個檔案叫做 DateTime.go,那麼就把這個模組放在 gqlgen.yml 描述中:

# 額外 (第三方以上) 的純量模組使用
models:
    ID:
        model:
            - github.com/99designs/gqlgen/graphql.ID
            - github.com/99designs/gqlgen/graphql.Int
            - github.com/99designs/gqlgen/graphql.Int64
            - github.com/99designs/gqlgen/graphql.Int32
    Int:
        model:
            - github.com/99designs/gqlgen/graphql.Int
            - github.com/99designs/gqlgen/graphql.Int64
            - github.com/99designs/gqlgen/graphql.Int32
    DateTime:
        model:
            - bitbucket.org/xxx/xxx.DateTime # 指向你的 package 位置,斜線到該檔案目錄,用 . 連接裡面的 Type 名稱。 (注意名稱也是有大小寫敏感)


完成後,做 gqlgen generate 就可以看到自動產生的 models_gen.go, resolvers 裡面的 struct 應該都會換成你自訂的型別。


References:

https://gqlgen.com/reference/scalars/
https://blog.laisky.com/p/gqlgen/
https://medium.com/@rtw0913/%E6%B7%BA%E8%AB%87-graphql-%E8%87%AA%E8%A8%82%E7%B4%94%E9%87%8F%E8%AE%8A%E6%95%B8-70c36f9f35b8

沒有留言:

張貼留言

© Mac Taylor, 歡迎自由轉貼。
Background Email Pattern by Toby Elliott
Since 2014