自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Facebook:如何在Golang中搭建GraphQL?

開發(fā) 前端
本文將重點介紹GraphQL的主要功能,以及就API而言它存在的優(yōu)缺點。文末將展示一個使用Golang的簡單程序(已搭建GraphQL)。

本文轉(zhuǎn)載自公眾號“讀芯術(shù)”(ID:AI_Discovery)。

多年來,人們一直在使用REST API來滿足開發(fā)需求,但得完成大量不必要的調(diào)用后,開發(fā)者才能靈活使用。例如,如果Web和移動設(shè)備所需的數(shù)據(jù)不同,我們還須針對Web和移動設(shè)備創(chuàng)建兩個不同的端點。

因此,F(xiàn)acebook創(chuàng)建了一種查詢語言——GraphQL,該語言可以準確地給出開發(fā)者查詢的內(nèi)容,干凈利落,也讓 API 更容易地隨著時間推移而演進,還能用于構(gòu)建強大的開發(fā)者工具。

本文將重點介紹GraphQL的主要功能,以及就API而言它存在的優(yōu)缺點。文末將展示一個使用Golang的簡單程序(已搭建GraphQL)。

什么是GraphQL?

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

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

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

Facebook:如何在Golang中搭建GraphQL?

GraphQL的操作

1. 查詢(Query)

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

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

Facebook:如何在Golang中搭建GraphQL?

2. 模式(Schema)

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

3. 分解器(Resolver)

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

4. 突變(Mutation)

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

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

Facebook:如何在Golang中搭建GraphQL?

5. 訂閱(Subscription)

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

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

Facebook:如何在Golang中搭建GraphQL?

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

Facebook:如何在Golang中搭建GraphQL?

1. 優(yōu)勢

(1) 開發(fā)迅速

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

Facebook:如何在Golang中搭建GraphQL?

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

Facebook:如何在Golang中搭建GraphQL?

使用以下示例GraphQL查詢:

Facebook:如何在Golang中搭建GraphQL?

(2) 靈活性

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

Facebook:如何在Golang中搭建GraphQL?

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

Facebook:如何在Golang中搭建GraphQL?

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

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

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

2. 缺點

  • 處理文件上傳:GraphQL規(guī)范中沒有關(guān)于文件上傳的內(nèi)容,并且突變不接受參數(shù)中的文件。
  • 簡單的API:如果你的API非常簡單,那GraphQL只會使其復(fù)雜,所以使用REST API可能會更好。

代碼實現(xiàn)

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

Facebook:如何在Golang中搭建GraphQL?

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

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

  1. app: 
  2. name: "GraphQL Test" 
  3. debug: true 
  4. port: "8080" 
  5. host: "localhost" 
  6. service: "http" 
  7. context: 
  8. timeout: 2 
  9. databases: 
  10. mongodb: 
  11. name: "local_db" 
  12. connection: "mongodb://root:root@localhost:27017" 

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

(1) databaseConfiguration.go

  1. package infrastructureimport( 
  2.    "context" 
  3.   "go.mongodb.org/mongo-driver/mongo" 
  4.   "go.mongodb.org/mongo-driver/mongo/options" 
  5.    "log" 
  6. )var Mongodb *mongo.Databasefunc(e *Environment) InitMongoDB()(db *mongo.Database, err error) { 
  7. clientOptions :=options.Client().ApplyURI(e.Databases["mongodb"].Connection) 
  8.    client, err :mongo.Connect(context.TODO(),clientOptions) 
  9.    err = client.Ping(context.TODO(), nil) 
  10. if err != nil { 
  11. return db, err 
  12.    } 
  13. Mongodb = client.Database(e.Databases["mongodb"].Name) 
  14. log.Println("Mongodb Ready!!!") 
  15. return db, err 

(2) environmentConfiguration.go

  1. package infrastructureimport( 
  2.    "io/ioutil" 
  3.    "log" 
  4.    "os" 
  5.    "path" 
  6.   "runtime""gopkg.in/yaml.v2" 
  7. )func(env *Environment) SetEnvironment() { 
  8.    _, filename, _, _ :runtime.Caller(1) 
  9. env.path = path.Join(path.Dir(filename),"environment/Connection.yml") 
  10.    _, err :os.Stat(env.path) 
  11. if err != nil { 
  12.       panic(err) 
  13. return 
  14. }func(env *Environment) LoadConfig() { 
  15.    content, err :=ioutil.ReadFile(env.path) 
  16. if err != nil { 
  17. log.Println(err) 
  18.       panic(err) 
  19.    } 
  20.    err =yaml.Unmarshal([]byte(string(content)), env) 
  21. if err != nil { 
  22. log.Println(err) 
  23.       panic(err) 
  24.    } 
  25. if env.App.Debug == false { 
  26. log.SetOutput(ioutil.Discard) 
  27.    } 
  28. log.Println("Config load successfully!") 
  29. return 

(3) model.go

  1. package infrastructuretypeapp struct{ 
  2. Appname     string `yaml:"name"` 
  3.    Debug       bool  `yaml:"debug"` 
  4.    Port        string `yaml:"port"` 
  5.    Service     string `yaml:"service"` 
  6.    Host        string `yaml:"host"` 
  7. }type database struct { 
  8.    Name       string `yaml:"name"` 
  9.    Connection string`yaml:"connection"` 
  10. }type Environment struct { 
  11.    App       app                 `yaml:"app"` 
  12.    Databases map[string]database`yaml:"databases"` 
  13.    path      string 

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

Facebook:如何在Golang中搭建GraphQL?

model.go:

  1. package 
  2. package booktypeBook struct { 
  3.    Name        string 
  4.    Price       string 
  5.    Description string 
  6. } booktypeBook struct {   Name        string   Price       string   Description string} 

resolver.go:

  1. package bookimport( 
  2.   "context""github.com/graphql-go/graphql" 
  3. )var productType = graphql.NewObject( 
  4. graphql.ObjectConfig{ 
  5.       Name: "Book", 
  6.       Fields: graphql.Fields{ 
  7.          "name": &graphql.Field{ 
  8.             Type: graphql.String, 
  9.          }, 
  10.          "price":&graphql.Field{ 
  11.             Type: graphql.String, 
  12.          }, 
  13.          "description":&graphql.Field{ 
  14.             Type: graphql.String, 
  15.          }, 
  16.       }, 
  17.    }, 
  18. )var queryType = graphql.NewObject( 
  19. graphql.ObjectConfig{ 
  20.       Name: "Query", 
  21.       Fields: graphql.Fields{ 
  22.          "book":&graphql.Field{ 
  23.             Type:        productType, 
  24.             Description: "Get bookby name", 
  25. Args: graphql.FieldConfigArgument{ 
  26.                "name":&graphql.ArgumentConfig{ 
  27.                   Type: graphql.String, 
  28.                }, 
  29.             }, 
  30.             Resolve: func(pgraphql.ResolveParams) (interface{}, error) { 
  31. var result interface{} 
  32.                name, ok :=p.Args["name"].(string) 
  33. if ok { 
  34.                   // Find product 
  35.                   result =GetBookByName(context.Background(), name) 
  36.                } 
  37. return result, nil 
  38.             }, 
  39.          }, 
  40.          "list":&graphql.Field{ 
  41.             Type:        graphql.NewList(productType), 
  42.             Description: "Get booklist", 
  43. Args: graphql.FieldConfigArgument{ 
  44.                "limit":&graphql.ArgumentConfig{ 
  45.                   Type: graphql.Int, 
  46.                }, 
  47.             }, 
  48.             Resolve: func(paramsgraphql.ResolveParams) (interface{}, error) { 
  49. var result interface{} 
  50.                limit, _ :=params.Args["limit"].(int) 
  51.                result =GetBookList(context.Background(), limit) 
  52. return result, nil 
  53.             }, 
  54.          }, 
  55.       }, 
  56.    })var mutationType =graphql.NewObject(graphql.ObjectConfig{ 
  57.    Name: "Mutation", 
  58.    Fields: graphql.Fields{ 
  59.       "create":&graphql.Field{ 
  60.          Type:        productType, 
  61.          Description: "Create newbook", 
  62. Args: graphql.FieldConfigArgument{ 
  63.             "name":&graphql.ArgumentConfig{ 
  64.                Type:graphql.NewNonNull(graphql.String), 
  65.             }, 
  66.             "price":&graphql.ArgumentConfig{ 
  67.                Type:graphql.NewNonNull(graphql.String), 
  68.             }, 
  69.             "description":&graphql.ArgumentConfig{ 
  70.                Type:graphql.NewNonNull(graphql.String), 
  71.             }, 
  72.          }, 
  73.          Resolve: func(paramsgraphql.ResolveParams) (interface{}, error) { 
  74.             book :Book
  75.                Name:        params.Args["name"].(string), 
  76.                Price:       params.Args["price"].(string), 
  77.                Description:params.Args["description"].(string), 
  78.             } 
  79. if err :InsertBook(context.Background(), book); err != nil { 
  80. return nil, err 
  81.             }return book, nil 
  82.          }, 
  83.       },"update":&graphql.Field{ 
  84.          Type:        productType, 
  85.          Description: "Update bookby name", 
  86. Args: graphql.FieldConfigArgument{ 
  87.             "name":&graphql.ArgumentConfig{ 
  88.                Type:graphql.NewNonNull(graphql.String), 
  89.             }, 
  90.             "price":&graphql.ArgumentConfig{ 
  91.                Type: graphql.String, 
  92.             }, 
  93.             "description":&graphql.ArgumentConfig{ 
  94.                Type: graphql.String, 
  95.             }, 
  96.          }, 
  97.          Resolve: func(paramsgraphql.ResolveParams) (interface{}, error) { 
  98.             book :Book{} 
  99. if name, nameOk :params.Args["name"].(string); nameOk { 
  100. book.Name = name 
  101.             } 
  102. if price, priceOk :params.Args["price"].(string); priceOk { 
  103. book.Price = price 
  104.             } 
  105. if description, descriptionOk :=params.Args["description"].(string); descriptionOk { 
  106. book.Description = description 
  107.             }if err :=UpdateBook(context.Background(), book); err != nil { 
  108. return nil, err 
  109.             } 
  110. return book, nil 
  111.          }, 
  112.       },"delete": &graphql.Field{ 
  113.          Type:        productType, 
  114.          Description: "Delete bookby name", 
  115. Args: graphql.FieldConfigArgument{ 
  116.             "name":&graphql.ArgumentConfig{ 
  117.                Type:graphql.NewNonNull(graphql.String), 
  118.             }, 
  119.          }, 
  120.          Resolve: func(paramsgraphql.ResolveParams) (interface{}, error) { 
  121.             name, _ :=params.Args["name"].(string) 
  122. if err :DeleteBook(context.Background(), name); err != nil { 
  123. return nil, err 
  124.             } 
  125. return name, nil 
  126.          }, 
  127.       }, 
  128.    }, 
  129. })// schema 
  130. var Schema, _ = graphql.NewSchema( 
  131. graphql.SchemaConfig{ 
  132.       Query:    queryType, 
  133.       Mutation: mutationType, 
  134.    }, 

repository.go:

  1. package bookimport( 
  2.    "context" 
  3.    "log""graphql/infrastructure""go.mongodb.org/mongo-driver/bson" 
  4.   "go.mongodb.org/mongo-driver/mongo/options" 
  5. )funcGetBookByName(ctxcontext.Context, name string) (result interface{}){ 
  6. var book Book 
  7.    data :=infrastructure.Mongodb.Collection("booklist").FindOne(ctx,bson.M{"name": name}) 
  8. data.Decode(&book) 
  9. return book 
  10. }funcGetBookList(ctxcontext.Context, limit int) (result interface{}){ 
  11. var book Book 
  12. var books []Bookoption :options.Find().SetLimit(int64(limit))cur, err:infrastructure.Mongodb.Collection("booklist").Find(ctx, bson.M{},option) 
  13. defer cur.Close(ctx) 
  14. if err != nil { 
  15. log.Println(err) 
  16. return nil 
  17.    } 
  18. for cur.Next(ctx) { 
  19. cur.Decode(&book) 
  20.       books = append(books, book) 
  21.    } 
  22. return books 
  23. }funcInsertBook(ctxcontext.Context, book Book) error { 
  24.    _, err :=infrastructure.Mongodb.Collection("booklist").InsertOne(ctx, book) 
  25. return err 
  26. }funcUpdateBook(ctxcontext.Context, book Book) error { 
  27.    filter :bson.M{"name":book.Name} 
  28.    update :bson.M{"$set":book} 
  29. upsertBool :true 
  30. updateOption :options.UpdateOptions{ 
  31. Upsert: &upsertBool, 
  32.    } 
  33.    _, err :=infrastructure.Mongodb.Collection("booklist").UpdateOne(ctx, filter,update, &updateOption) 
  34. return err 
  35. }funcDeleteBook(ctxcontext.Context, name string) error { 
  36.    _, err :=infrastructure.Mongodb.Collection("booklist").DeleteOne(ctx,bson.M{"name": name}) 
  37. return err 

response.go:

  1. package bookimport( 
  2.    "encoding/json" 
  3.    "net/http" 
  4.    "time" 
  5. )type SetResponsestruct { 
  6.    Status     string     `json:"status"` 
  7.    Data       interface{} `json:"data,omitempty"` 
  8. AccessTime string     `json:"accessTime"` 
  9. }funcHttpResponseSuccess(w http.ResponseWriter, r *http.Request, data interface{}){ 
  10. setResponse :SetResponse
  11.       Status:     http.StatusText(200), 
  12. AccessTime: time.Now().Format("02-01-2006 15:04:05"), 
  13.       Data:       data} 
  14.    response, _ :=json.Marshal(setResponse) 
  15. w.Header().Set("Content-Type", "Application/json") 
  16. w.WriteHeader(200) 
  17. w.Write(response) 
  18. }funcHttpResponseError(w http.ResponseWriter, r *http.Request, data interface{},code int) { 
  19. setResponse :SetResponse
  20.       Status:     http.StatusText(code), 
  21. AccessTime: time.Now().Format("02-01-2006 15:04:05"), 
  22.       Data:       data} 
  23.    response, _ :=json.Marshal(setResponse) 
  24. w.Header().Set("Content-Type", "Application/json") 
  25. w.WriteHeader(code) 
  26. w.Write(response) 

routes.go:

  1. package bookimport( 
  2.    "github.com/go-chi/chi" 
  3.   "github.com/go-chi/chi/middleware" 
  4.   "github.com/graphql-go/handler" 
  5. )funcRegisterRoutes(r *chi.Mux) *chi.Mux { 
  6.    /* GraphQL */ 
  7. graphQL :handler.New(&handler.Config{ 
  8.       Schema:   &Schema, 
  9.       Pretty:   true, 
  10. GraphiQL: true, 
  11.    }) 
  12. r.Use(middleware.Logger) 
  13. r.Handle("/query", graphQL) 
  14. return r 

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

main.go:

  1. package mainimport( 
  2.    "github.com/go-chi/chi" 
  3.    "graphql/book" 
  4.    "graphql/infrastructure" 
  5.    "log" 
  6.    "net/http" 
  7.    "net/url" 
  8. )funcmain() { 
  9.    routes :chi.NewRouter() 
  10.    r :book.RegisterRoutes(routes) 
  11. log.Println("Server ready at 8080") 
  12. log.Fatal(http.ListenAndServe(":8080", r)) 
  13. }funcinit() { 
  14. val :url.Values{} 
  15. val.Add("parseTime", "1") 
  16. val.Add("loc", "Asia/Jakarta") 
  17.    env :infrastructure.Environment{} 
  18. env.SetEnvironment() 
  19. env.LoadConfig() 
  20. env.InitMongoDB() 

運行程序的結(jié)果如下:

 

Facebook:如何在Golang中搭建GraphQL?

 

 

創(chuàng)建書目詳情示例

 

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

 

責(zé)任編輯:趙寧寧 來源: 今日頭條
相關(guān)推薦

2022-01-21 10:58:39

JavaScriptGolangPython

2024-11-12 08:00:00

LSM樹GolangMemTable

2021-05-07 09:06:55

GraphQLAPI 以太坊

2022-10-08 11:39:56

斷路器Golang項目

2022-03-28 08:00:00

數(shù)據(jù)庫GraphQL公共云

2015-05-25 09:13:31

NTP網(wǎng)絡(luò)時間協(xié)議NTP服務(wù)器

2022-01-05 18:19:30

容器鏡像Golang

2020-01-18 14:55:03

架構(gòu)運維技術(shù)

2022-03-30 18:18:33

GolangTiDB數(shù)據(jù)庫

2018-11-08 09:00:31

樹莓派WordPressLinux

2023-11-09 09:13:48

GraphQLAPI 架構(gòu)

2015-11-09 11:23:09

FacebookFOOMs

2016-07-26 13:58:52

Ubuntulinux網(wǎng)橋

2020-06-24 07:00:00

GraphQL API監(jiān)控

2018-07-03 09:48:30

Facebook 開發(fā)Python

2014-04-15 15:14:49

UbuntuGhost博客平臺

2015-06-03 15:12:07

云端TFS微軟代碼管理環(huán)境

2019-05-09 09:00:00

WindowsKafka

2016-08-05 15:04:33

javascripthtmljs

2023-10-22 20:20:37

FiberGo
點贊
收藏

51CTO技術(shù)棧公眾號