zoukankan      html  css  js  c++  java
  • 在 golang 中构建 GraphQL 服务器 ggenql

    官方文档地址 :https://gqlgen.com/getting-started/

    本教程将带您完成使用 gqlgen 构建 GraphQL 服务器的过程,该服务器可以:

    • 返回待办事项列表
    • 创建新的待办事项
    • 在待办事项完成时标记它们

    您可以在此处找到本教程的完成代码

    设置项目

    为您的项目创建一个目录,并将其初始化为 Go Module:

    $ mkdir gqlgen-todos
    $ cd gqlgen-todos
    $ go mod init github.com/[username]/gqlgen-todos
    $ go get github.com/99designs/gqlgen
    

    构建服务器

    创建项目骨架

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

    这将创建我们建议的包布局。如果需要,您可以在 gqlgen.yml 中修改这些路径。

    ├── go.mod
    ├── go.sum
    ├── gqlgen.yml               - The gqlgen config file, knobs for controlling the generated code.
    ├── graph
    │   ├── generated            - A package that only contains the generated runtime
    │   │   └── generated.go
    │   ├── model                - A package for all your graph models, generated or otherwise
    │   │   └── models_gen.go
    │   ├── resolver.go          - The root graph resolver type. This file wont get regenerated
    │   ├── schema.graphqls      - Some schema. You can split the schema into as many graphql files as you like
    │   └── schema.resolvers.go  - the resolver implementation for schema.graphql
    └── server.go                - The entry point to your app. Customize it however you see fit
    

    定义您的架构

    gqlgen 是一个模式优先的库——在编写代码之前,你使用 GraphQL模式定义语言来描述你的 API 默认情况下,这会进入一个名为的文件, schema.graphql但您可以根据需要将其分解为多个不同的文件。

    为我们生成的模式是:

    type Todo {
      id: ID!
      text: String!
      done: Boolean!
      user: User!
    }
    
    type User {
      id: ID!
      name: String!
    }
    
    type Query {
      todos: [Todo!]!
    }
    
    input NewTodo {
      text: String!
      userId: String!
    }
    
    type Mutation {
      createTodo(input: NewTodo!): Todo!
    }

    实现解析器

    执行时,gqlgen 的generate命令将模式文件 ( graph/schema.graphqls) 与模型进行比较graph/model/*,并且,只要可能,它将直接绑定到模型。这已经在init运行时完成了我们将在本教程的后面编辑模式,但现在,让我们看看已经生成的内容。

    如果我们看一看,graph/schema.resolvers.go我们将看到 gqlgen 无法匹配它们的所有时间。对我们来说是两次:

    func (r *mutationResolver) CreateTodo(ctx context.Context, input model.NewTodo) (*model.Todo, error) {
    	panic(fmt.Errorf("not implemented"))
    }
    
    func (r *queryResolver) Todos(ctx context.Context) ([]*model.Todo, error) {
    	panic(fmt.Errorf("not implemented"))
    }
    

    我们只需要实现这两个方法来让我们的服务器工作:

    首先我们需要一个地方来跟踪我们的状态,让我们把它放在graph/resolver.gograph/resolver.go文件是我们声明应用程序依赖项的地方,例如我们的数据库。server.go当我们创建图形时,它会被初始化一次

    type Resolver struct{
    	todos []*model.Todo
    }
    

    回到graph/schema.resolvers.go,让我们实现那些自动生成的解析器函数的主体。对于CreateTodo,我们将使用math.rand随机生成的 ID 简单地返回一个待办事项,并将其存储在内存中的待办事项列表中——在实际应用中,您可能会使用数据库或其他一些后端服务。

    func (r *mutationResolver) CreateTodo(ctx context.Context, input model.NewTodo) (*model.Todo, error) {
    	todo := &model.Todo{
    		Text:   input.Text,
    		ID:     fmt.Sprintf("T%d", rand.Int()),
    		User: &model.User{ID: input.UserID, Name: "user " + input.UserID},
    	}
    	r.todos = append(r.todos, todo)
    	return todo, nil
    }
    
    func (r *queryResolver) Todos(ctx context.Context) ([]*model.Todo, error) {
    	return r.todos, nil
    }
    

    运行服务器

    我们现在有一个工作服务器,可以启动它:

    go run server.go
    

    在浏览器中打开 http://localhost:8080。以下是一些要尝试的查询,从创建待办事项开始:

    mutation createTodo {
      createTodo(input: { text: "todo", userId: "1" }) {
        user {
          id
        }
        text
        done
      }
    }

    然后查询它:

    query findTodos {
      todos {
        text
        done
        user {
          name
        }
      }
    }

    不要急切地获取用户

    这个例子很棒,但在现实世界中获取大多数对象的成本很高。我们不想在待办事项上加载用户,除非用户确实要求它。所以让我们用Todo更真实的东西替换生成的模型。

    创建一个名为的新文件 graph/model/todo.go

    package model
    
    type Todo struct {
    	ID     string `json:"id"`
    	Text   string `json:"text"`
    	Done   bool   `json:"done"`
    	UserID string `json:"user"`
    }
    

    笔记

    默认情况下,gqlgen 将使用模型目录中与名称匹配的任何模型,这可以在gqlgen.yml.

    并运行go run github.com/99designs/gqlgen generate

    现在,如果我们查看,graph/schema.resolvers.go我们可以看到一个新的解析器,让我们实现它并修复CreateTodo.

    func (r *mutationResolver) CreateTodo(ctx context.Context, input model.NewTodo) (*model.Todo, error) {
    	todo := &model.Todo{
    		Text:   input.Text,
    		ID:     fmt.Sprintf("T%d", rand.Int()),
    		UserID: input.UserID, // fix this line
    	}
    	r.todos = append(r.todos, todo)
    	return todo, nil
    }
    
    func (r *todoResolver) User(ctx context.Context, obj *model.Todo) (*model.User, error) {
    	return &model.User{ID: obj.UserID, Name: "user " + obj.UserID}, nil
    }
    

    收尾工作

    在我们的resolver.gopackage之间import,添加以下行:

    //go:generate go run github.com/99designs/gqlgen
    

    这个神奇的注释告诉go generate我们要重新生成代码时要运行的命令。要在整个项目中递归运行 go generate,请使用以下命令:

    go generate ./...

    失败 执行 go get github.com/99designs/gqlgen/cmd@v0.14.0
  • 相关阅读:
    [LeetCode] Power of Three 判断3的次方数
    [LeetCode] 322. Coin Change 硬币找零
    [LeetCode] 321. Create Maximum Number 创建最大数
    ITK 3.20.1 VS2010 Configuration 配置
    VTK 5.10.1 VS2010 Configuration 配置
    FLTK 1.3.3 MinGW 4.9.1 Configuration 配置
    FLTK 1.1.10 VS2010 Configuration 配置
    Inheritance, Association, Aggregation, and Composition 类的继承,关联,聚合和组合的区别
    [LeetCode] Bulb Switcher 灯泡开关
    [LeetCode] Maximum Product of Word Lengths 单词长度的最大积
  • 原文地址:https://www.cnblogs.com/brady-wang/p/15361694.html
Copyright © 2011-2022 走看看