graphql GO学习
设计API参数列表
type User { id: ID email: String! post(id: ID!): Post posts: [Post!]! follower(id: ID!): User followers: [User!]! followee(id: ID!): User followees: [User!]! }type Post { id: ID user: User! title: String! body: String! comment(id: ID!): Comment comments: [Comment!]! } type Comment { id: ID user: User! post: Post! title: String body: String! }
[Type]!
或者[Type!]
或者[Type!]!
(请仔细看这里!
的位置),它们的含义分别为:
- 列表本身为必填项,但其内部元素可以为空
- 列表本身可以为空,但是其内部元素为必填
- 列表本身和内部元素均为必填
转换为go的实现(User为例)
var UserType = graphql.NewObject(graphql.ObjectConfig{ Name: "User", Fields: graphql.Fields{ "id": &graphql.Field{ Type: graphql.NewNonNull(graphql.ID), Resolve: func(p graphql.ResolveParams) (interface{}, error) { if user, ok := p.Source.(*User); ok == true { return user.ID, nil } return nil, nil }, }, "email": &graphql.Field{ Type: graphql.NewNonNull(graphql.String), Resolve: func(p graphql.ResolveParams) (interface{}, error) { if user, ok := p.Source.(*User); ok == true { return user.Email, nil } return nil, nil }, }, }, }) func init() { UserType.AddFieldConfig("post", &graphql.Field{ Type: PostType, Args: graphql.FieldConfigArgument{ "id": &graphql.ArgumentConfig{ Description: "Post ID", Type: graphql.NewNonNull(graphql.ID), }, }, Resolve: func(p graphql.ResolveParams) (interface{}, error) { if user, ok := p.Source.(*User); ok == true { i := p.Args["id"].(string) id, err := strconv.Atoi(i) if err != nil { return nil, err } return GetPostByIDAndUser(id, user.ID) } return nil, nil }, }) UserType.AddFieldConfig("posts", &graphql.Field{ Type: graphql.NewNonNull(graphql.NewList(graphql.NewNonNull(PostType))), Resolve: func(p graphql.ResolveParams) (interface{}, error) { if user, ok := p.Source.(*User); ok == true { return GetPostsForUser(user.ID) } return []Post{}, nil }, }) UserType.AddFieldConfig("follower", &graphql.Field{ Type: UserType, Args: graphql.FieldConfigArgument{ "id": &graphql.ArgumentConfig{ Description: "Follower ID", Type: graphql.NewNonNull(graphql.ID), }, }, Resolve: func(p graphql.ResolveParams) (interface{}, error) { if user, ok := p.Source.(*User); ok == true { i := p.Args["id"].(string) id, err := strconv.Atoi(i) if err != nil { return nil, err } return GetFollowerByIDAndUser(id, user.ID) } return nil, nil }, }) UserType.AddFieldConfig("followers", &graphql.Field{ Type: graphql.NewNonNull(graphql.NewList(graphql.NewNonNull(UserType))), Resolve: func(p graphql.ResolveParams) (interface{}, error) { if user, ok := p.Source.(*User); ok == true { return GetFollowersForUser(user.ID) } return []User{}, nil }, }) UserType.AddFieldConfig("followee", &graphql.Field{ Type: UserType, Args: graphql.FieldConfigArgument{ "id": &graphql.ArgumentConfig{ Description: "Followee ID", Type: graphql.NewNonNull(graphql.ID), }, }, Resolve: func(p graphql.ResolveParams) (interface{}, error) { if user, ok := p.Source.(*User); ok == true { i := p.Args["id"].(string) id, err := strconv.Atoi(i) if err != nil { return nil, err } return GetFolloweeByIDAndUser(id, user.ID) } return nil, nil }, }) UserType.AddFieldConfig("followees", &graphql.Field{ Type: graphql.NewNonNull(graphql.NewList(graphql.NewNonNull(UserType))), Resolve: func(p graphql.ResolveParams) (interface{}, error) { if user, ok := p.Source.(*User); ok == true { return GetFolloweesForUser(user.ID) } return []User{}, nil }, }) }
API查询
graphql设计模式有三种查询类型
- query(查询):当获取数据时,应当选取Query类型
- mutation(更改):当尝试修改数据时,应当使用mutation类型
- subscription(订阅):当希望数据更改时,可以进行消息推送,使用subscription类型
query和mutation设计如下:
type Query { user(id: ID!): User } type Mutation { createUser(email: String!): User removeUser(id: ID!): Boolean follow(follower: ID!, followee: ID!): Boolean unfollow(follower: ID!, followee: ID!): Boolean createPost(user: ID!, title: String!, body: String!): Post removePost(id: ID!): Boolean createComment(user: ID!, post: ID!, title: String!, body: String!): Comment removeComment(id: ID!): Boolean }
转换为go的实现
var QueryType = graphql.NewObject(graphql.ObjectConfig{ Name: "Query", Fields: graphql.Fields{ "user": &graphql.Field{ Type: UserType, Args: graphql.FieldConfigArgument{ "id": &graphql.ArgumentConfig{ Description: "User ID", Type: graphql.NewNonNull(graphql.ID), }, }, Resolve: func(p graphql.ResolveParams) (interface{}, error) { i := p.Args["id"].(string) id, err := strconv.Atoi(i) if err != nil { return nil, err } return GetUserByID(id) }, }, }, })var MutationType = graphql.NewObject(graphql.ObjectConfig{ Name: "Mutation", Fields: graphql.Fields{ "createUser": &graphql.Field{ Type: UserType, Args: graphql.FieldConfigArgument{ "email": &graphql.ArgumentConfig{ Description: "New User Email", Type: graphql.NewNonNull(graphql.String), }, }, Resolve: func(p graphql.ResolveParams) (interface{}, error) { email := p.Args["email"].(string) user := &User{ Email: email, } err := InsertUser(user) return user, err }, }, "removeUser": &graphql.Field{ Type: graphql.Boolean, Args: graphql.FieldConfigArgument{ "id": &graphql.ArgumentConfig{ Description: "User ID to remove", Type: graphql.NewNonNull(graphql.ID), }, }, Resolve: func(p graphql.ResolveParams) (interface{}, error) { i := p.Args["id"].(string) id, err := strconv.Atoi(i) if err != nil { return nil, err } err = RemoveUserByID(id) return (err == nil), err }, }, }, })
接口的启用也很简单
func main() { schema, err := graphql.NewSchema(graphql.SchemaConfig{ Query: QueryType, Mutation: MutationType, }) if err != nil { log.Fatal(err) } http.Handle("/graphql", handler(schema)) log.Fatal(http.ListenAndServe("0.0.0.0:8080", nil)) }func handler(schema graphql.Schema) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { query, err := ioutil.ReadAll(r.Body) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } result := graphql.Do(graphql.Params{ Schema: schema, RequestString: string(query), }) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(result) } }
请求实例