溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Golang中怎么搭建一個GraphQL

發(fā)布時間:2021-07-06 15:19:27 來源:億速云 閱讀:288 作者:Leah 欄目:web開發(fā)

本篇文章為大家展示了Golang中怎么搭建一個GraphQL,內容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細介紹希望你能有所收獲。

什么是GraphQL?

GraphQL是用于API的查詢語言,它是服務器端運行時,通過為數(shù)據定義的類型系統(tǒng)執(zhí)行查詢。

GraphQL是一種查詢語言,適用許多領域,但通常用來在客戶端和服務器應用程序之間搭橋。無所謂使用的是哪個網絡層,所以可以在客戶端和服務器應用程序之間讀取和寫入數(shù)據。(RobinWieruch《GraphQL指南》)

雖然GraphQL是查詢語言,但它與數(shù)據庫沒有直接關系,也就是GraphQL不限于任意SQL或是NoSQL的數(shù)據庫。GraphQL位于客戶端和服務器端,通過API連接/訪問。開發(fā)這種查詢語言的目的之一是通過提供所需的數(shù)據來促進后端、前端或移動應用程序之間的數(shù)據通信。

Golang中怎么搭建一個GraphQL

GraphQL的操作

1. 查詢(Query)

查詢用于讀取或獲取值。無論哪種情況,操作都是一個簡單的字符串,GraphQL服務器可以解析該字符串并以特定格式的數(shù)據進行響應。

你可以使用查詢操作從API請求數(shù)據。查詢描述需要從GraphQL服務器獲取的數(shù)據,發(fā)送查詢其實是按字段要求提取數(shù)據。(Eve Porcello、Alex  Banks著《學習GraphQL》)

Golang中怎么搭建一個GraphQL

2. 模式(Schema)

GraphQL使用Schema描述數(shù)據圖的形狀。這樣的Schema定義類型的層次結構,依托的是從后端數(shù)據存儲區(qū)填充的字段,也準確表示客戶端可以對數(shù)據圖執(zhí)行哪些查詢和突變。

3. 分解器(Resolver)

分解器是負責為Schema單一字段填充數(shù)據的功能。它可以用你定義的任何方式填充該數(shù)據,例如從后端數(shù)據庫或第三方API提取數(shù)據。

4. 突變(Mutation)

修改數(shù)據存儲中的數(shù)據并返回一個值,它可用于插入、更新或刪除數(shù)據。

突變與查詢原理相同:它具有字段和對象、參數(shù)和變量、片段和操作名稱,以及返回結果的指令和嵌套對象。(Robin  Wieruch著《GraphQL之路》)

Golang中怎么搭建一個GraphQL

5. 訂閱(Subscription)

將數(shù)據從服務器推送到客戶端的方法是選擇偵聽來自服務器的實時消息。

GraphQL的訂閱來自Facebook的真實用例。開發(fā)團隊希望找到一種方法,不刷新頁面就能實時顯示發(fā)文獲得的有效點贊(Live Likes)。(Eve  Porcello、Alex Banks著《學習GraphQL》)

Golang中怎么搭建一個GraphQL

GraphQL的優(yōu)勢與劣勢

Golang中怎么搭建一個GraphQL

1. 優(yōu)勢

(1) 開發(fā)迅速

來看一個案例:如何得到圖書借閱者的數(shù)據。在視圖中,首先我要顯示書籍列表,書籍列表菜單顯示中出現(xiàn)一個借閱者的列表。在REST  API中,需要創(chuàng)建新的端點以返回圖書清單,再創(chuàng)建一個新的端點以返回每本書的借閱人。

Golang中怎么搭建一個GraphQL

與REST API不同,GraphQL中僅使用一個端點就可以返回書籍列表和借閱者列表了。

Golang中怎么搭建一個GraphQL

使用以下示例GraphQL查詢:

Golang中怎么搭建一個GraphQL

(2) 靈活性

來看一個案例:如何獲取書籍詳細信息。在網絡視圖上,我想展示書籍詳細信息,例如名稱、價格和介紹。在REST  API中需要創(chuàng)建一個新的端點以返回名稱、價格、介紹等的書籍詳細信息。

Golang中怎么搭建一個GraphQL

如果在移動端查看時,只想展示圖書詳細信息中的名稱和價格怎么辦?如果使用與Web視圖相同的端點,則會浪費介紹的數(shù)據。所以需要更改該端點內部的現(xiàn)有邏輯,或創(chuàng)建一個新的端點。

Golang中怎么搭建一個GraphQL

與REST API不同,GraphQL中僅使用一個端點即可按照Web或移動設備的需求返回書籍詳細信息。在GraphQL中,只需更改查詢。

(3) 維護簡單,易于使用

  • Rest API:如果客戶端需要其他數(shù)據,通常需要添加一個新端點或更改一個現(xiàn)有端點。

  • GraphQL:客戶只需要更改查詢。

2. 缺點

  • 處理文件上傳:GraphQL規(guī)范中沒有關于文件上傳的內容,并且突變不接受參數(shù)中的文件。

  • 簡單的API:如果你的API非常簡單,那GraphQL只會使其復雜,所以使用REST API可能會更好。

代碼實現(xiàn)

實現(xiàn)過程使用了Golang編程語言,這里是項目架構:

Golang中怎么搭建一個GraphQL

在依賴版本和依賴管理功能上使用的是go模塊。用graphql-go來支持查詢、突變和訂閱;用graphql-go-handler來支持處理器。此時,我將創(chuàng)建一個簡單的程序,這里使用GraphQL為詳細書目創(chuàng)建CRUD。步驟如下:

先新建一個環(huán)境文件夾,然后新建一個名為connection.yml的文件:

app: name: "GraphQL Test" debug: true port: "8080" host: "localhost" service: "http" context: timeout: 2 databases: mongodb: name: "local_db" connection: "mongodb://root:root@localhost:27017"

然后創(chuàng)建一個架構文件夾,創(chuàng)建名為databaseConfiguration.go、environmentConfiguration.go和model.go的文件。這個文件夾用來配置數(shù)據庫并從connection.yml讀取數(shù)據。

(1) databaseConfiguration.go

package infrastructureimport(    "context"   "go.mongodb.org/mongo-driver/mongo"   "go.mongodb.org/mongo-driver/mongo/options"    "log" )var Mongodb *mongo.Databasefunc(e *Environment) InitMongoDB()(db *mongo.Database, err error) { clientOptions :=options.Client().ApplyURI(e.Databases["mongodb"].Connection)    client, err := mongo.Connect(context.TODO(),clientOptions)    err = client.Ping(context.TODO(), nil) if err != nil { return db, err    } Mongodb = client.Database(e.Databases["mongodb"].Name) log.Println("Mongodb Ready!!!") return db, err }

(2) environmentConfiguration.go

package infrastructureimport(    "io/ioutil"    "log"    "os"    "path"   "runtime""gopkg.in/yaml.v2" )func(env *Environment) SetEnvironment() {    _, filename, _, _ := runtime.Caller(1) env.path = path.Join(path.Dir(filename),"environment/Connection.yml")    _, err := os.Stat(env.path) if err != nil {       panic(err) return } }func(env *Environment) LoadConfig() {    content, err :=ioutil.ReadFile(env.path) if err != nil { log.Println(err)       panic(err)    }    err =yaml.Unmarshal([]byte(string(content)), env) if err != nil { log.Println(err)       panic(err)    } if env.App.Debug == false { log.SetOutput(ioutil.Discard)    } log.Println("Config load successfully!") return }

(3) model.go

package infrastructuretypeapp struct{ Appname     string `yaml:"name"`    Debug       bool  `yaml:"debug"`    Port        string `yaml:"port"`    Service     string `yaml:"service"`    Host        string `yaml:"host"` }type database struct {    Name       string `yaml:"name"`    Connection string`yaml:"connection"` }type Environment struct {    App       app                 `yaml:"app"`    Databases map[string]database`yaml:"databases"`    path      string }

第三,創(chuàng)建一個書目文件夾,創(chuàng)建如下文件:

Golang中怎么搭建一個GraphQL

model.go:

package package booktypeBook struct {    Name        string    Price       string    Description string } booktypeBook struct {   Name        string   Price       string   Description string}

resolver.go:

package bookimport(   "context""github.com/graphql-go/graphql" )var productType = graphql.NewObject( graphql.ObjectConfig{       Name: "Book",       Fields: graphql.Fields{          "name": &graphql.Field{             Type: graphql.String,          },          "price":&graphql.Field{             Type: graphql.String,          },          "description":&graphql.Field{             Type: graphql.String,          },       },    }, )var queryType = graphql.NewObject( graphql.ObjectConfig{       Name: "Query",       Fields: graphql.Fields{          "book":&graphql.Field{             Type:        productType,             Description: "Get bookby name", Args: graphql.FieldConfigArgument{                "name":&graphql.ArgumentConfig{                   Type: graphql.String,                },             },             Resolve: func(pgraphql.ResolveParams) (interface{}, error) { var result interface{}                name, ok :=p.Args["name"].(string) if ok {                   // Find product                   result =GetBookByName(context.Background(), name)                } return result, nil             },          },          "list":&graphql.Field{             Type:        graphql.NewList(productType),             Description: "Get booklist", Args: graphql.FieldConfigArgument{                "limit":&graphql.ArgumentConfig{                   Type: graphql.Int,                },             },             Resolve: func(paramsgraphql.ResolveParams) (interface{}, error) { var result interface{}                limit, _ :=params.Args["limit"].(int)                result =GetBookList(context.Background(), limit) return result, nil             },          },       },    })var mutationType =graphql.NewObject(graphql.ObjectConfig{    Name: "Mutation",    Fields: graphql.Fields{       "create":&graphql.Field{          Type:        productType,          Description: "Create newbook", Args: graphql.FieldConfigArgument{             "name":&graphql.ArgumentConfig{                Type:graphql.NewNonNull(graphql.String),             },             "price":&graphql.ArgumentConfig{                Type:graphql.NewNonNull(graphql.String),             },             "description":&graphql.ArgumentConfig{                Type:graphql.NewNonNull(graphql.String),             },          },          Resolve: func(paramsgraphql.ResolveParams) (interface{}, error) {             book := Book{                Name:        params.Args["name"].(string),                Price:       params.Args["price"].(string),                Description:params.Args["description"].(string),             } if err := InsertBook(context.Background(), book); err != nil { return nil, err             }return book, nil          },       },"update":&graphql.Field{          Type:        productType,          Description: "Update bookby name", Args: graphql.FieldConfigArgument{             "name":&graphql.ArgumentConfig{                Type:graphql.NewNonNull(graphql.String),             },             "price":&graphql.ArgumentConfig{                Type: graphql.String,             },             "description":&graphql.ArgumentConfig{                Type: graphql.String,             },          },          Resolve: func(paramsgraphql.ResolveParams) (interface{}, error) {             book := Book{} if name, nameOk := params.Args["name"].(string); nameOk { book.Name = name             } if price, priceOk := params.Args["price"].(string); priceOk { book.Price = price             } if description, descriptionOk :=params.Args["description"].(string); descriptionOk { book.Description = description             }if err :=UpdateBook(context.Background(), book); err != nil { return nil, err             } return book, nil          },       },"delete": &graphql.Field{          Type:        productType,          Description: "Delete bookby name", Args: graphql.FieldConfigArgument{             "name":&graphql.ArgumentConfig{                Type:graphql.NewNonNull(graphql.String),             },          },          Resolve: func(paramsgraphql.ResolveParams) (interface{}, error) {             name, _ :=params.Args["name"].(string) if err := DeleteBook(context.Background(), name); err != nil { return nil, err             } return name, nil          },       },    }, })// schema var Schema, _ = graphql.NewSchema( graphql.SchemaConfig{       Query:    queryType,       Mutation: mutationType,    }, )

repository.go:

package bookimport(    "context"    "log""graphql/infrastructure""go.mongodb.org/mongo-driver/bson"   "go.mongodb.org/mongo-driver/mongo/options" )funcGetBookByName(ctxcontext.Context, name string) (result interface{}){ var book Book    data :=infrastructure.Mongodb.Collection("booklist").FindOne(ctx,bson.M{"name": name}) data.Decode(&book) return book }funcGetBookList(ctxcontext.Context, limit int) (result interface{}){ var book Book var books []Bookoption := options.Find().SetLimit(int64(limit))cur, err:= infrastructure.Mongodb.Collection("booklist").Find(ctx, bson.M{},option) defer cur.Close(ctx) if err != nil { log.Println(err) return nil    } for cur.Next(ctx) { cur.Decode(&book)       books = append(books, book)    } return books }funcInsertBook(ctxcontext.Context, book Book) error {    _, err :=infrastructure.Mongodb.Collection("booklist").InsertOne(ctx, book) return err }funcUpdateBook(ctxcontext.Context, book Book) error {    filter := bson.M{"name":book.Name}    update := bson.M{"$set":book} upsertBool := true updateOption := options.UpdateOptions{ Upsert: &upsertBool,    }    _, err :=infrastructure.Mongodb.Collection("booklist").UpdateOne(ctx, filter,update, &updateOption) return err }funcDeleteBook(ctxcontext.Context, name string) error {    _, err :=infrastructure.Mongodb.Collection("booklist").DeleteOne(ctx,bson.M{"name": name}) return err }

response.go:

package bookimport(    "encoding/json"    "net/http"    "time" )type SetResponsestruct {    Status     string     `json:"status"`    Data       interface{} `json:"data,omitempty"` AccessTime string     `json:"accessTime"` }funcHttpResponseSuccess(w http.ResponseWriter, r *http.Request, data interface{}){ setResponse := SetResponse{       Status:     http.StatusText(200), AccessTime: time.Now().Format("02-01-2006 15:04:05"),       Data:       data}    response, _ :=json.Marshal(setResponse) w.Header().Set("Content-Type", "Application/json") w.WriteHeader(200) w.Write(response) }funcHttpResponseError(w http.ResponseWriter, r *http.Request, data interface{},code int) { setResponse := SetResponse{       Status:     http.StatusText(code), AccessTime: time.Now().Format("02-01-2006 15:04:05"),       Data:       data}    response, _ :=json.Marshal(setResponse) w.Header().Set("Content-Type", "Application/json") w.WriteHeader(code) w.Write(response) }

routes.go:

package bookimport(    "github.com/go-chi/chi"   "github.com/go-chi/chi/middleware"   "github.com/graphql-go/handler" )funcRegisterRoutes(r *chi.Mux) *chi.Mux {    /* GraphQL */ graphQL := handler.New(&handler.Config{       Schema:   &Schema,       Pretty:   true, GraphiQL: true,    }) r.Use(middleware.Logger) r.Handle("/query", graphQL) return r }

最后,創(chuàng)建名為 main.go的文件。

main.go:

package mainimport(    "github.com/go-chi/chi"    "graphql/book"    "graphql/infrastructure"    "log"    "net/http"    "net/url" )funcmain() {    routes := chi.NewRouter()    r := book.RegisterRoutes(routes) log.Println("Server ready at 8080") log.Fatal(http.ListenAndServe(":8080", r)) }funcinit() { val := url.Values{} val.Add("parseTime", "1") val.Add("loc", "Asia/Jakarta")    env := infrastructure.Environment{} env.SetEnvironment() env.LoadConfig() env.InitMongoDB() }

運行程序的結果如下:

Golang中怎么搭建一個GraphQL
創(chuàng)建書目詳情示例

GraphQL有很多優(yōu)點,但事實證明,與REST  API相比,GraphQL處理文件上傳和簡單API的性能表現(xiàn)有所不足。因此,我們必須首先了解要構建的系統(tǒng),是否適合將GraphQL用作應用程序的設計架構。

上述內容就是Golang中怎么搭建一個GraphQL,你們學到知識或技能了嗎?如果還想學到更多技能或者豐富自己的知識儲備,歡迎關注億速云行業(yè)資訊頻道。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI