Tuesday, March 22, 2022

[GraphQL] 使用Golang Gin Framework來建置GraphQL的API Server

 [前言]

本篇內容將提供快速的指引,使用Golang Gin Framework來建置GraphQL的API  Server。


[使用Golang Gin Framework來建置GraphQL的API  Server]

GraphQL API Server主體上來說是參考下列文章的方式來建置:

第 1 天 - API 服務器(Go,GraphQL) - 第 1 部分

第 2 天 - API 服務器(Go,GraphQL) - 第 2 部分

Golang Gin Server網路上已經有很多資源,我們假設已經有的前提下,如何來建置GraphQL的API  Server。首先,先安裝所需的模組:

#在個人自己的Gin Server 的 project 內
$ go get github.com/99designs/gqlgen
$ go get github.com/gin-gonic/gin

我們需要準備下列檔案: 

$ tree -L 1
.
├── gqlgen.yml
├── internal
├── scripts
 ......

gqlgen.yml 的內容如下:

# go-gql-server gqlgen.yml file
 # Refer to https://gqlgen.com/config/
 # for detailed .gqlgen.yml documentation.

 schema:
   - internal/gql/schemas/schema.graphql
 # Let gqlgen know where to put the generated server
 exec:
   filename: internal/gql/generated/generated.go
   package: gql
 # Let gqlgen know where to put the generated models (if any)
 model:
   filename: internal/gql/models/generated/generated.go
   package: models
 # Let gqlgen know where to put the generated resolvers
 resolver:
   filename: internal/gql/resolvers/generated/generated.go
   type: Resolver
   package: resolvers
 autobind: []

scripts/gqlgen.sh 的內容如下:

#!/bin/bash
 printf "\nRegenerating gqlgen files\n"
 rm -f internal/gql/generated/generated.go \
     internal/gql/models/generated/generated.go \
     internal/gql/resolvers/generated/generated.go
 time gqlgen $1

 cp internal/gql/generated/generated.go internal/gql
 mv internal/gql/generated.go internal/gql/main.go
 cp internal/gql/models/generated/generated.go internal/gql/models
 mv internal/gql/models/generated.go internal/gql/models/users.go
 cp internal/gql/resolvers/generated/generated.go internal/gql/resolvers
 mv internal/gql/resolvers/generated.go internal/gql/resolvers/main.go

 printf "\nDone.\n\n"

透過執行 scripts/gqlgen.sh, 會generate codes (包含 generated、models、resolvers、schemas),並放在internal資料夾內,其檔案樹狀結構如下:

$ tree -L 3
.
├── gql
│   ├── generated
│   │   └── generated.go
│   ├── main.go
│   ├── models
│   │   ├── generated
│   │   └── users.go
│   ├── resolvers
│   │   ├── generated
│   │   └── main.go
│   └── schemas
│       └── schema.graphql
└── handlers
    └── gql.go

備註: 

GraphQL api server using golang Gin framework 有提到可以用下列方式產生初步的GraphQL骨架,所以不一定要用上述的方式

$ go run github.com/99designs/gqlgen init

我們需要實作相關檔案:

internal/handlers/gql.go

package handlers

import (
    "microsegmentation/internal/gql"
    "microsegmentation/internal/gql/resolvers"

    "github.com/99designs/gqlgen/handler"
    "github.com/gin-gonic/gin"
)

// GraphqlHandler defines the GQLGen GraphQL server handler
func GraphqlHandler() gin.HandlerFunc {
    // NewExecutableSchema and Config are in the generated.go file
    c := gql.Config{
        Resolvers: &resolvers.Resolver{},
    }

    h := handler.GraphQL(gql.NewExecutableSchema(c))

    return func(c *gin.Context) {
        h.ServeHTTP(c.Writer, c.Request)
    }
}

// PlaygroundHandler Defines the Playground handler to expose our playground
func PlaygroundHandler(path string) gin.HandlerFunc {
    h := handler.Playground("Go GraphQL Server", path)
    return func(c *gin.Context) {
        h.ServeHTTP(c.Writer, c.Request)
    }
}

internal/resolvers/main.go

package resolvers

import (
    "context"

    // remeber to change the import path
    gql "microsegmentation/internal/gql"
    models "microsegmentation/internal/gql/models"
)

// THIS CODE IS A STARTING POINT ONLY. IT WILL NOT BE UPDATED WITH SCHEMA CHANGES.

type Resolver struct{}

func (r *Resolver) Mutation() gql.MutationResolver {
    return &mutationResolver{r}
}
func (r *Resolver) Query() gql.QueryResolver {
    return &queryResolver{r}
}

type mutationResolver struct{ *Resolver }

func (r *mutationResolver) CreateUser(ctx context.Context, input models.UserInput) (*models.User, error) {
    panic("not implemented")
}
func (r *mutationResolver) UpdateUser(ctx context.Context, input models.UserInput) (*models.User, error) {
    panic("not implemented")
}
func (r *mutationResolver) DeleteUser(ctx context.Context, userID string) (bool, error) {
    panic("not implemented")
}

type queryResolver struct{ *Resolver }

func (r *queryResolver) Users(ctx context.Context, userID *string) ([]*models.User, error) {
    // this is for test purpose
    var tempID = "ec17af15-e354-440c-a09f-69715fc8b595"
    var tempEmail = "your@email.com"
    var tempUserID = "UserID-1"
    records := []*models.User{
        &models.User{
            ID:     &tempID,
            Email:  &tempEmail,
            UserID: &tempUserID,
        },
    }
    return records, nil
}


完成後,就可以在自己的Gin Server上加入GraphQL相關的API Routes

    //GraphQL
    // Playground handler
    r.GET("/heartbeat", handlers.Heartbeat())
    r.GET("/gql", handlers.PlaygroundHandler("/gql"))
    r.POST("/gql", handlers.GraphqlHandler())


透過瀏覽器 http://<<your ip address>>/gql 即可看到下列畫面:


[相關其他參考]

有許多線上資源可以參考,在此不再贅述。請參考下列相關連結:

GraphQL api server using golang Gin framework

Schema

    • Schema is the way to provide structure of the object or resource. Based on the schema we can query or mutate the data/resource on the GraphqL server.

Queries

    • Queries are used to retrieve the data from the GraphQL server.

Mutations

    • Mutations are used to update/modify the resource on the server.

Resolvers

    • Resolvers are used to create the data structure that matches with the provided resource schema.

在這篇文章: 使用 GraphQL 滿足現代業務需求 

  • GraphQL 架構

  • GraphQL 的特徵:
    • 可描述性的:使用GraphQL,你獲取的都是你想要的數據,很少也不會少;
    • 請求的對象,請求的對象的一個簡單的對象:GraphQL自然遵循間的關係,我們可以通過簡單的對象請求,我們獲取到一個我們相關的對象,通過簡單的對象,可以獲取一個和她創建的所有文章,然後可以獲取文章的所有評論;
    • 強類型的:使用GraphQL的類型系統,我們可以描述被服務器查詢的可能的數據,確保從服務器獲取到的數據和我們查詢的一致;
    • 不做限制:現在並沒有綁定到某種語言的某種語言,實際上已經有了某種語言;
    • 後台代碼:QL不存在特定的數據庫,甚至可以使用連接關聯的API。
    • 好反省的:GraphQL服務器查詢架構的細節。


GraphQL


No comments: