在Golang上创建一个graphql后端

今天,我们将在Golang + GraphQL中开发一个应用程序。

我们经常在我们的项目上使用GraphQL,并且对它了解很多,并与各种编程语言(Javascript,Ruby)一起使用,现在我们已经可以尝试使用Golang GraphQL软件包了。

关于GraphQL查询语言在Internet上的优势,已经有很多说法,许多人称赞它在服务器端和客户端使用时的简便性,灵活性和便利性。

为了使用GraphQL进行方便的开发,经常配置GraphiQL或GraphQL Playground-用于发送请求和查看API文档的接口。
要在本地部署这样的Playground,只需使用此开源解决方案-graphl-go的Golang HTTP.Handler。

graphql游乐场Golang

它看起来像正在奔跑的游乐场。

让我们继续编写一个小型应用程序,其中的一个示例将说明如何使用GraphQL和Go。完整的应用程序代码可以在Github上的链接上找到

首先,我们将启动服务器和游乐场。

func main() {
  schema, err := graphql.NewSchema(defineSchema()) //      
  if err != nil {
     log.Panic("Error when creating the graphQL schema", err)
  }

  h := handler.New(&handler.Config{
     Schema:     &schema,
     Pretty:     true,
     GraphiQL:   false,
     Playground: true,
  }) //      FormatErrorFn -    

  http.Handle("/graphql", h) //      playground    
  err = http.ListenAndServe(":8080", nil)
  if err != nil {
     log.Panic("Error when starting the http server", err)
  }
}

graphql后端不能没有该方案的描述,现在我们将分析其描述。

var User = graphql.NewObject(
  graphql.ObjectConfig{
     Name: "User",
     Fields: graphql.Fields{
        "_id": &graphql.Field{
           Type: ObjectID,
        },
        "firstName": &graphql.Field{
           Type: graphql.String,
        },
        "lastName": &graphql.Field{
           Type: graphql.String,
        },
        "email": &graphql.Field{
           Type: graphql.String,
        },
     },
  },
)

var UserInput = graphql.NewInputObject(
  graphql.InputObjectConfig{
     Name: "UserInput",
     Fields: graphql.InputObjectConfigFieldMap{
        "firstName": &graphql.InputObjectFieldConfig{
           Type: graphql.String,
        },
        "lastName": &graphql.InputObjectFieldConfig{
           Type: graphql.Int,
        },
        "email": &graphql.InputObjectFieldConfig{
           Type: graphql.String,
        },
     },
  },
)

func defineSchema() graphql.SchemaConfig {
  return graphql.SchemaConfig{
     Query: graphql.NewObject(graphql.ObjectConfig{
        Name: "Query",
        Fields: graphql.Fields{
           "users": &graphql.Field{
              Name:    "users",
              Type:    graphql.NewList(User),
              Resolve: usersResolver,
           },
        },
     }),
     Mutation: graphql.NewObject(graphql.ObjectConfig{
        Name: "Mutation",
        Fields: graphql.Fields{
           "addUser": &graphql.Field{
              Name:    "addUser",
              Type:    User,
              Resolve: addUserResolver,
              Args: graphql.FieldConfigArgument{
                 "input": &graphql.ArgumentConfig{
                    Type: UserInput,
                 },
              },
           },
        },
     })}
}

上面,我们用_id,firstName,lastName和email字段描述了User类型,将其用作对请求的响应类型。_id字段的类型为ObjectID,这是一种自定义类型,因为有必要序列化本机ObjectID类型的MongoDB,这是这种类型的结构。

type InsertOneResult struct {
  InsertedID interface{}
}

为了描述用于添加用户的突变的输入参数,创建了UserInput类型,其中包含3个描述我们用户的可选字段。_id字段在此类型中不存在,因为它将在解析器中生成。

要连接到mongodb,此项目使用golang mongodb驱动程序。

func usersCollection() *mongo.Collection { // ,   users    
  ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)
  client, err := mongo.NewClient(options.Client().ApplyURI("mongodb://mongo:27017"))
  if err != nil {
     log.Panic("Error when creating mongodb connection client", err)
  }
  collection := client.Database("testing").Collection("users")
  err = client.Connect(ctx)
  if err != nil {
     log.Panic("Error when connecting to mongodb", err)
  }

  return collection
}


//      
func usersResolver(_ graphql.ResolveParams) (interface{}, error) {
  ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)
  collection := usersCollection()
  result, err := collection.Find(ctx, bson.D{})
  if err != nil {
     log.Print("Error when finding user", err)
     return nil, err
  }

  defer result.Close(ctx)

  var r []bson.M
  err = result.All(ctx, &r)
  if err != nil {
     log.Print("Error when reading users from cursor", err)
  }

  return r, nil
}


//     
func addUserResolver(p graphql.ResolveParams) (interface{}, error) {
  ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)
  collection := usersCollection()
  //         ,   _id
  id, err := collection.InsertOne(ctx, p.Args["input"])
  if err != nil {
     log.Print("Error when inserting user", err)
     return nil, err
  }

  var result bson.M
  //       _id
  err = collection.FindOne(ctx, bson.M{"_id": id.InsertedID}).Decode(&result)
  if err != nil {
     log.Print("Error when finding the inserted user by its id", err)
     return nil, err
  }

  return result, nil
}

在这里,我们使用mongodb作为存储用户数据的地方,处理了获取和突变用户的请求。除了GraphQL并没有限制我们选择数据库之外,您还可以使用其他任何版本,而不会出现任何问题,包括非关系型和关系型。

我使用docker compose捆绑了golang和mongodb。为此,我描述了一个小的设置文件。 都准备好了。 一对golang mongo技术使我们能够以方便的形式存储用户数据,以便从GraphQL查询中进一步返回。

version: '3'
services:
graphql:
image: golang
volumes:
- .:/go/src
command: /bin/bash -c "cd src && go run *.go"
ports:
- 8080:8080
mongo:
image: mongo





当然,该应用程序非常简单紧凑。它可以作为您下一个项目的基础,并补充有新的类型,请求和变异以及其他功能。这次我们没有考虑实现一些更有趣的GraphQL功能(例如订阅)的示例。也许我将来会继续这个例子,所以如果您在本文中缺少某些内容,请留下评论,在下一部分中,我们一定会考虑的。

All Articles