Golang中怎么搭建一个GraphQL

发布时间:2021-07-06 15:19:27 作者:Leah
来源:亿速云 阅读:292

本篇文章为大家展示了Golang中怎么搭建一个GraphQL,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。

什么是GraphQL?

GraphQL是用于API的查询语言,它是服务器端运行时,通过为数据定义的类型系统执行查询。

GraphQL是一种查询语言,适用许多领域,但通常用来在客户端和服务器应用程序之间搭桥。无所谓使用的是哪个网络层,所以可以在客户端和服务器应用程序之间读取和写入数据。(RobinWieruch《GraphQL指南》)

虽然GraphQL是查询语言,但它与数据库没有直接关系,也就是GraphQL不限于任意SQL或是NoSQL的数据库。GraphQL位于客户端和服务器端,通过API连接/访问。开发这种查询语言的目的之一是通过提供所需的数据来促进后端、前端或移动应用程序之间的数据通信。

Golang中怎么搭建一个GraphQL

GraphQL的操作

1. 查询(Query)

查询用于读取或获取值。无论哪种情况,操作都是一个简单的字符串,GraphQL服务器可以解析该字符串并以特定格式的数据进行响应。

你可以使用查询操作从API请求数据。查询描述需要从GraphQL服务器获取的数据,发送查询其实是按字段要求提取数据。(Eve Porcello、Alex  Banks著《学习GraphQL》)

Golang中怎么搭建一个GraphQL

2. 模式(Schema)

GraphQL使用Schema描述数据图的形状。这样的Schema定义类型的层次结构,依托的是从后端数据存储区填充的字段,也准确表示客户端可以对数据图执行哪些查询和突变。

3. 分解器(Resolver)

分解器是负责为Schema单一字段填充数据的功能。它可以用你定义的任何方式填充该数据,例如从后端数据库或第三方API提取数据。

4. 突变(Mutation)

修改数据存储中的数据并返回一个值,它可用于插入、更新或删除数据。

突变与查询原理相同:它具有字段和对象、参数和变量、片段和操作名称,以及返回结果的指令和嵌套对象。(Robin  Wieruch著《GraphQL之路》)

Golang中怎么搭建一个GraphQL

5. 订阅(Subscription)

将数据从服务器推送到客户端的方法是选择侦听来自服务器的实时消息。

GraphQL的订阅来自Facebook的真实用例。开发团队希望找到一种方法,不刷新页面就能实时显示发文获得的有效点赞(Live Likes)。(Eve  Porcello、Alex Banks著《学习GraphQL》)

Golang中怎么搭建一个GraphQL

GraphQL的优势与劣势

Golang中怎么搭建一个GraphQL

1. 优势

(1) 开发迅速

来看一个案例:如何得到图书借阅者的数据。在视图中,首先我要显示书籍列表,书籍列表菜单显示中出现一个借阅者的列表。在REST  API中,需要创建新的端点以返回图书清单,再创建一个新的端点以返回每本书的借阅人。

Golang中怎么搭建一个GraphQL

与REST API不同,GraphQL中仅使用一个端点就可以返回书籍列表和借阅者列表了。

Golang中怎么搭建一个GraphQL

使用以下示例GraphQL查询:

Golang中怎么搭建一个GraphQL

(2) 灵活性

来看一个案例:如何获取书籍详细信息。在网络视图上,我想展示书籍详细信息,例如名称、价格和介绍。在REST  API中需要创建一个新的端点以返回名称、价格、介绍等的书籍详细信息。

Golang中怎么搭建一个GraphQL

如果在移动端查看时,只想展示图书详细信息中的名称和价格怎么办?如果使用与Web视图相同的端点,则会浪费介绍的数据。所以需要更改该端点内部的现有逻辑,或创建一个新的端点。

Golang中怎么搭建一个GraphQL

与REST API不同,GraphQL中仅使用一个端点即可按照Web或移动设备的需求返回书籍详细信息。在GraphQL中,只需更改查询。

(3) 维护简单,易于使用

2. 缺点

代码实现

实现过程使用了Golang编程语言,这里是项目架构:

Golang中怎么搭建一个GraphQL

在依赖版本和依赖管理功能上使用的是go模块。用graphql-go来支持查询、突变和订阅;用graphql-go-handler来支持处理器。此时,我将创建一个简单的程序,这里使用GraphQL为详细书目创建CRUD。步骤如下:

先新建一个环境文件夹,然后新建一个名为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"

然后创建一个架构文件夹,创建名为databaseConfiguration.go、environmentConfiguration.go和model.go的文件。这个文件夹用来配置数据库并从connection.yml读取数据。

(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 }

第三,创建一个书目文件夹,创建如下文件:

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 }

最后,创建名为 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
创建书目详情示例

GraphQL有很多优点,但事实证明,与REST  API相比,GraphQL处理文件上传和简单API的性能表现有所不足。因此,我们必须首先了解要构建的系统,是否适合将GraphQL用作应用程序的设计架构。

上述内容就是Golang中怎么搭建一个GraphQL,你们学到知识或技能了吗?如果还想学到更多技能或者丰富自己的知识储备,欢迎关注亿速云行业资讯频道。

推荐阅读:
  1. 怎么搭建golang框架
  2. 如何搭建golang框架

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

golang

上一篇:Golang中make和new如何使用

下一篇:golang中内存分配的原理是什么

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》