Is there an example of how to configure nested fields similar to the below (JS) ? I tried converting this js example to go but it didn't end well. Thanks
query:
{
posts {
title,
author {
name
}
}
}
Hi @kmulvey
Maybe you could share how far you've gotten into the go version, we could help you better that way :)
In the meantime, this is how I would do:
type RootQuery {
post: Post
}
type Post {
_id: String!
title: String
content: String
author: Author
}
type Author {
_id: String!
name: String
}
Hope this helps you move into the right direction, let me know if you need further help.
Cheers!
package main
import (
"github.com/graphql-go/graphql"
"fmt"
)
// Define GraphQL Schema
var AuthorObject = graphql.NewObject(graphql.ObjectConfig{
Name: "Author",
Description: "This respresents an Author",
Fields: graphql.Fields{
"_id": &graphql.Field{
Type: graphql.NewNonNull(graphql.String),
},
"name": &graphql.Field{
Type: graphql.String,
},
},
})
var PostObject = graphql.NewObject(graphql.ObjectConfig{
Name: "Post",
Description: "This respresents an Post",
Fields: graphql.Fields{
"_id": &graphql.Field{
Type: graphql.NewNonNull(graphql.String),
},
"title": &graphql.Field{
Type: graphql.NewNonNull(graphql.String),
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
// get title from source
title, _ := p.Source["title"].(string)
if title == "" {
// return error
return nil, fmt.Errorf("Does not exists")
}
return title, nil
},
},
"content": &graphql.Field{
Type: graphql.String,
},
"author": &graphql.Field{
Type: AuthorObject,
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
// get title from source
title, _ := p.Source["title"].(string)
// add business logic to retrieve find for given post title
return author, nil
},
},
},
})
var RootQuery = graphql.NewObject(graphql.ObjectConfig{
Name: "BlogSchema",
Description: "Root of the Blog Schema",
Fields: graphql.Fields{
"posts": &graphql.Field{
Type: graphql.NewList(PostObject),
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
// add business logic to retrieve list of Posts
return PostsList
},
},
"echo": &graphql.Field{
Type: graphql.String,
Description: "Echo what you enter",
Args: graphql.FieldConfigArgument{
"message": &graphql.ArgumentConfig{
Type: graphql.String,
},
},
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
// get arg value
message, _ := p.Args["message"].(string)
return message, nil
},
},
},
})
thanks @sogko for the quick reply. I was able to advance your example a bit and get the correct structure out but now the values are coming out null. I have a feeling I made a silly mistake somewhere but can't seem to spot it. Also authorObject (commented out) won't compile if i leave the "posts" section in. Thanks for your help.
https://gist.github.com/kmulvey/4843061d16492a24ce2663e497dda528
{
"data": {
"posts": [
{
"author": {
"name": null
},
"title": null
},
{
"author": {
"name": null
},
"title": null
},
etc ...
]
}
}
hi @kmulvey
Actually you almost had it.
Since you are returning Golang structs in Resolve() functions, you need to let graphql-go know how to map the struct fields to your GraphQL fields.
Currently we use json tags for this.
(This is shown in the examples, but unfortunately we have not done a good job with the documentation to highlight this)
Simply update your struct implementations with the json tags, mapping it to the field names defined in your schema. (Note the capitalisation)
// User respresents an author
type User struct {
ID int `json:"_id"`
Age int `json:"age"`
Name string `json:"name"`
Posts []Post `json:"posts"`
}
// Post is a blog post
type Post struct {
ID int `json:"_id"`
UserID int `gorm:"index"` // json-tag not needed since you are not using this field currently
Title string `gorm:"type:varchar(100);unique_index" json:"title"`
}
{
"data": {
"posts": [
{
"author": {
"name": "userOne"
},
"title": "post one"
},
{
"author": {
"name": "userOne"
},
"title": "post two"
},
{
"author": {
"name": "userTwo"
},
"title": "post three"
},
{
"author": {
"name": "userTwo"
},
"title": "post four"
}
]
}
}
If I find the time, I can do a complete implementation of the example gist you gave earlier, to compare notes.
Cheers!
What if I need to perform both
{posts {title,author {name}}}
and
{user {name,posts {title}}}
queries?
@artshpakov
query posts {
posts {
title
author {
name
}
}
user {
name
posts {
title
}
}
}
I'm not sure your question is related to the issue you posted in. Nor am I sure exactly what you're asking.
@bbuck
Sorry if I misunderstood some graphql concepts in general, but I was referring to the golang code snippet above. It shows how to implement your Post and User types in case you want to query posts with authors embedded. So you've got to have your AuthorObject to be defined when you define PostObject. What I'm asking about is the case when you need both posts with nesting author
posts{title text author{name}} and authors with nested posts author(id:1){name posts{title text}}. You cannot have a "posts" field on AuthorObject because you have no PostObject defined yet.
Like, in Ruby graphql gem you can have that with
PostType = GraphQL::ObjectType.define do
field :title, !types.String
field :text, !types.String
field :author, types[UserType] do
resolve -> (obj, args, ctx) { User.find(obj.user_id) }
end
end
UserType = GraphQL::ObjectType.define do
field :name, !types.String
field :posts, types[PostType] do
resolve -> (obj, args, ctx) { obj.posts }
end
end
QueryType = GraphQL::ObjectType.define do
field :posts, types[PostType] do
resolve ->(obj, args, ctx) { Post.all }
end
field :user do
type UserType
argument :id, !types.ID
resolve -> (obj, args, ctx) { User.find(args["id"]) }
end
end
What I'm asking is if there's any way to do the same with graphql-go.
@artshpakov Ah, okay. So basically, in the case of types that have to reference other types and the requirement for the type to be non-null in GraphQL you'd delay field definition.
Define your Post type without Authors, and define your Authors type without posts, then once the two exist without reference each other use Object.AddFieldConfig to tie them together (add a posts to Author and add a author to Post).
@bbuck I see. That will quickly become clumsy though -- at least when your APIs start to grow and you decide to have your type definitions in separate files.
We use it in production and don't see any issues with it. I'm not sure how it can become clumsy.
Sorry to revive an old thread - I can open a new issue but it's related to this comment: https://github.com/graphql-go/graphql/issues/129#issuecomment-219200124.
In the example, the RootQuery has a posts field that has a Resolve that fetches all the posts and returns them.
My understanding is that then the PostObject resolvers start trying to figure out the _id, title, content and author. The author resolver uses p.Source to determine which author to look up and return.
I have a local example of something very similar, but for the author resolve function, p.Source is []Posts where I expected it to be a single Post and just have graphql-go call my resolver once for each Post the initial resolver received.
Is this a graphql thing?
How am I supposed to use p.Source to determine a particular author if I'm getting an array back?
Most helpful comment
Hi @kmulvey
Maybe you could share how far you've gotten into the go version, we could help you better that way :)
In the meantime, this is how I would do:
Note that I've left out the business / app logic to retrieve/find posts/author etc.
Hope this helps you move into the right direction, let me know if you need further help.
Cheers!