Monday, April 8, 2024

GraphQL Usage in Go


 


This post includes an example of GraphQL usage in Go. The actual GraphQL implementation is using the graphql-go library.


Some insights I got from this:

  • Implementing code in GraphQL is much more complicated than using REST. We need to re-write the specifications for each usage, and repeat the fields names. It is not build in to the language like GO and simple JSON marshaling.
  • To use mutation drop the root parenthesis in the query. OMG I've spent an hour trying to understand why it is not working.
  • I believe the advantage of GraphQL over REST is dynamic fields selection. Don't know many applications where this matters.
  • Authorization is not full built into GraphQL. I guess this is why we have so many security issues in applications using GraphQL.



package main

import (
"encoding/json"
"fmt"
"github.com/graphql-go/graphql"
)

type dbActor struct {
Name string
BirthYear int
}

var dbActors = []*dbActor{
{
Name: "John Travolta",
BirthYear: 1954,
},
{
Name: "Robert Redford",
BirthYear: 1936,
},
}

func main() {
schema := createSchema()
var query string

query = `
{
list{
name
birthYear
}
}
`
runQuery(schema, query)

query = `
{
actor(name: "Robert Redford"){
birthYear
}
}
`
runQuery(schema, query)

query = `
mutation {
create(name:"Meryl Streep",birthYear:1949){
name
}
}
`
runQuery(schema, query)

query = `
{
list{
name
birthYear
}
}
`
runQuery(schema, query)

}

func runQuery(schema graphql.Schema, query string) {
params := graphql.Params{
Schema: schema,
RequestString: query,
}

result := graphql.Do(params)
if len(result.Errors) > 0 {
panic(fmt.Errorf("failed to execute graphql operation, errors: %+v", result.Errors))
}

jsonData, err := json.MarshalIndent(result, "", " ")
if err != nil {
panic(err)
}

fmt.Printf("\n%s\n", jsonData)
}

func createSchema() graphql.Schema {
schemaActor := graphql.NewObject(graphql.ObjectConfig{
Name: "actor",
Fields: graphql.Fields{
"name": &graphql.Field{
Type: graphql.String,
},
"birthYear": &graphql.Field{
Type: graphql.Int,
},
},
})

schemaQuery := graphql.NewObject(graphql.ObjectConfig{
Name: "QueryRoot",
Fields: graphql.Fields{
"list": &graphql.Field{
Type: graphql.NewList(schemaActor),
Resolve: func(params graphql.ResolveParams) (interface{}, error) {
return dbActors, nil
},
},
"actor": &graphql.Field{
Type: schemaActor,
Args: graphql.FieldConfigArgument{
"name": &graphql.ArgumentConfig{
Type: graphql.String,
},
},
Resolve: func(params graphql.ResolveParams) (interface{}, error) {
name, ok := params.Args["name"].(string)
if !ok {
panic("name argument invalid")
}
for _, actor := range dbActors {
if actor.Name == name {
return actor, nil
}
}
return nil, nil
},
},
},
})

schemaMutation := graphql.NewObject(graphql.ObjectConfig{
Name: "Mutation",
Fields: graphql.Fields{
"create": &graphql.Field{
Type: schemaActor,
Args: graphql.FieldConfigArgument{
"name": &graphql.ArgumentConfig{
Type: graphql.NewNonNull(graphql.String),
},
"birthYear": &graphql.ArgumentConfig{
Type: graphql.NewNonNull(graphql.Int),
},
},
Resolve: func(params graphql.ResolveParams) (interface{}, error) {
name, ok := params.Args["name"].(string)
if !ok {
panic("name argument invalid")
}
birthYear, ok := params.Args["birthYear"].(int)
if !ok {
panic("birthYear argument invalid")
}
actor := dbActor{
Name: name,
BirthYear: birthYear,
}
dbActors = append(dbActors, &actor)
return actor, nil
},
},
},
})

schemaConfig := graphql.SchemaConfig{
Query: schemaQuery,
Mutation: schemaMutation,
}
schema, err := graphql.NewSchema(schemaConfig)
if err != nil {
panic(err)
}
return schema
}





No comments:

Post a Comment