当前位置: 首页 > 工具软件 > gqlgen > 使用案例 >

Gqlgen初步学习

尉迟远
2023-12-01

Gqlgen初步学习

本文是小编对gqlgen的初步构建做的整理,还望对你有所帮助。

一 简介

1,什么是gqlgen

gqlgen是一个用于构建GraphQL服务器的Go库。

2,gqlgen有什么用

序号作用
1gqlgen基于模式优先的方法 — 您可以使用GraphQL模式定义语言来定义API。
2gqlgen优先考虑类型安全 — 你不应该在这里看到map[string]interface{}。
3gqlgen启用了Codegen — 我们生成繁琐的bits,所以你可以专注于快速构建你的应用程序。

3,常见问题

3.1 我如何防止取回可能不被使用的子对象?

当你有这样的嵌套或递归模式:

type User {
  id: ID!
  name: String!
  friends: [User!]!
}

您需要告诉gqlgen,只有当用户请求它时,它才应该获取好友。有两种方法可以做到这一点;

3.1.1,使用自定义模型

编写一个省略friends字段的自定义模型:

type User struct {
  ID int
  Name string
}

并参考gqlgen.yml中的模型:

//gqlgen.yml
models:
  User:
    model: github.com/you/pkg/model.User 
    //去导入路径到上面的用户结构
3.1.2,使用显式解析器

如果您想继续使用生成的模型,请在gqlgen.yml中显式地将该字段标记为需要解析器:

// gqlgen.yml
models:
  User:
    fields:
      friends:
        resolver: true 
        // 强制生成解析器

在完成上述操作并运行generate后,我们需要为好友提供一个解析器:

func (r *userResolver) Friends(ctx context.Context, obj *User) ([]*User, error) {
  // select * from user where friendid = obj.ID
  return friends,  nil
}

你也可以使用内联配置指令来达到同样的效果:

directive @goModel(model: String, models: [String!]) on OBJECT
    | INPUT_OBJECT
    | SCALAR
    | ENUM
    | INTERFACE
    | UNION

directive @goField(forceResolver: Boolean, name: String) on INPUT_FIELD_DEFINITION
    | FIELD_DEFINITION

type User @goModel(model: "github.com/you/pkg/model.User") {
    id: ID!         @goField(name: "todoId")
    friends: [User!]!   @goField(forceResolver: true)
}
3.2 我可以改变类型的ID从字符串类型到Int类型吗?

你可以在配置中重新映射它,如下所示:

models:
  ID: // The GraphQL type ID is backed by
    model:
      - github.com/99designs/gqlgen/graphql.IntID
     //An go integer
      - github.com/99designs/gqlgen/graphql.ID 
     // or a go string

这意味着gqlgen将能够自动绑定到你自己编写的模型的字符串或整数,但是这个列表中的第一个模型将被用作默认类型,并且它将一直被使用

二 如何创建使用

1,创建

创建一个目录作为项目目录,并初始化它:

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

2,建立服务器

创建项目框架:

$ 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

3,定义type

qlgen是一个模式优先的库——在编写代码之前,您需要使用GraphQL模式定义语言来描述API。默认情况下,这将被放入一个名为schema的文件中。但是,您可以将它分解成任意多个不同的文件。

自动生成的模式是:

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

4,实现解析器

gqlgen generate将模式文件(graph/schema.graphqls)与模型graph/model/*进行比较,只要可以,它就会直接绑定到模型上。

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.go中:

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

当我们创建图表时,这是我们声明应用程序依赖的地方,比如数据库,它在服务器中初始化一次。

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

5,不要急于获取用户

这个例子很好,但是在现实世界中,获取大多数对象都很昂贵。我们不想把用户加载到todo上,除非用户确实要求这样做。因此,让我们用更真实的东西替换生成的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。我们可以看到一个新的解析器,我们来实现它并修复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
}

6,收尾

在resolver.go的顶部,在package和import之间添加以下一行:

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

这个注释单纯告诉我们,如果要要重新生成代码时,要运行什么命令。

至此,结束。本文翻译于gqlgen官网,可前往https://gqlgen.com/getting-started/查看原文

 类似资料: