Full Blog TOC

Full Blog Table Of Content with Keywords Available HERE

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