perjantai 2. kesäkuuta 2017

Trying out Graphql with Golang and Vue.js

I've made normal Rest APIs for many years now. For some time now, there has been interest for Facebooks GraphQL especially on the front-end side. I too have been curious about it but haven't had the change to play with it. So, I made a small project to test it and to test Vue.js too which also is new for me. I have used Angular a bit and more React but Vue.js was something totally new.

GraphQL with Go

There actually is a ready project for using GraphQL so it's quite simple to start using it on the backend side.

I couldn't think of any really good topic to make so I wrote some Star Wars characters into a file and used those.

var personType = graphql.NewObject(
  graphql.ObjectConfig{
    Name:   "person",
    Fields: fields,
  },
)

There is only one type and it's definition is simple enough.

var queryType = graphql.NewObject(graphql.ObjectConfig{
  Name: "RootQuery",
  Fields: graphql.Fields{
    "person": &graphql.Field{
      Type: personType,
      Args: args,
      Resolve: func(p graphql.ResolveParams) (interface{}, error) {
        return filterPerson(data, p.Args), nil
      },
    },
    "personList": &graphql.Field{
      Type: graphql.NewList(personType),
      Resolve: func(p graphql.ResolveParams) (interface{}, error) {
        return data, nil
      },
    },
  },
})

Now, the query type includes two fields to make queries. Person field filters the data with a query parameter from the request. PersonList field just returns the whole data or list of Star Wars characters.

schema, _ = graphql.NewSchema(
  graphql.SchemaConfig{
    Query: queryType,
  },
)

Schema defines the capabilities of the server with queryType.

func main() {
  err := importJSONDataFromFile(jsonDataFile)
  if err != nil {
    fmt.Printf("Error: %s\n", err.Error())
    return
  }

  http.HandleFunc("/graphql", func(w http.ResponseWriter, r *http.Request) {
    result := executeQuery(r.URL.Query()["query"][0], schema)
    w.Header().Add("Access-Control-Allow-Origin", "*")
    json.NewEncoder(w).Encode(result)
  })

  fs := http.FileServer(http.Dir("frontend/dist"))
  http.Handle("/", fs)

  fmt.Println("Now server is running on port 8000")
  http.ListenAndServe(":8000", nil)
}

The main function of the server does four things. First, it gets the Star Wars character json and makes the personType "fields" and queryType "data" available. Second, it sets listening endpoint to /graphql for queries. Third, it defines a file server for static content for root "/" endpoint. And for last, it starts the server.

At this point, qraphql queries already work. If your start the server, and query it for example with:

curl http://localhost:8000/graphql?query={person(name:%22Darth%22){id,name,surname}}

You get an answer:

{
  data: {
    person: {
      id: "1",
      name: "Darth",
      surname: "Vader"
    }
  }
}

Vue.js

To really test what the difference is between using ordinary Rest APIs and GraphQL I set up a small front-end to fetch and show the data.


I first set up the vue.js project under front-end directory, in production it would be delivered from there. In development it can be run separetily with hot reloading and the works.

cd frontend
npm install -g vue-cli
vue init webpack graphql-vue-example
mv graphql-vue-example/* .
rmdir graphql-vue-example
npm install
npm run dev

Now, this sets up an initial project, it needs some work to get all things like we want them. Almost everything that needs to be changed, is at "frontend/src/components/Persons.vue". It contains the html template, vue javascript for the page and scoped styles.

<template>
  <div class="personclass">
    <h1>{{ msg }}</h1>
    <input id="personName" type="text" value="Darth" v-model="personName"></input>
    <button class="btn btn-primary" v-on:click="getPerson()">Get Star wars person</button>
    <h2>One fetched person</h2>
    <div>{{ person.person.id }} {{ person.person.name }} {{ person.person.surname }}</div>
    <h2>List of all</h2>
    <div v-for="value in persons.personList">
      {{ value.id }} {{ value.name }} {{ value.surname }}
    </div>
  </div>
</template>

If you write a name of a character to the input field and then click the button, it goes and fetches the character in question. Of course, only if there is a match. There also is the whole list, which is fetched immediately when the page is rendered.

<script>
export default {
  name: 'personclass',
  data () {
    return {
      msg: 'Star Wars persons',
      person: {person: {id: '', name: '', surname: ''}},
      persons: ''
    }
  },
  mounted () {
    this.getPersons()
  },
  methods: {
    getPerson () {
      console.log(this.personName)
      this.$http.get('http://localhost:8000/graphql?query=' + 
          encodeURIComponent('{person(name:"' + this.personName + '"){id,name,surname}}'))
          .then(response => {
        this.person = JSON.parse(response.body).data
      }, response => {
        console.log(response)
      })
    },
    getPersons () {
      this.$http.get('http://localhost:8000/graphql?query=' + 
          encodeURIComponent('{personList{id,name,surname}}')).then(response => {
        this.persons = JSON.parse(response.body).data
      }, response => {
        console.log(response)
      })
    }
  }
}
</script>

The Vue javascript part is simple enough. There are two methods, getPersons gets all the Star Wars characters and it's called when the page is mounted. Method getPerson is called from the html page when the button is clicked and it returns only the character which is being searched.


The whole example project in github: https://github.com/jelinden/graphql-vue-example

References

https://facebook.github.io/react/blog/2015/05/01/graphql-introduction.html
https://github.com/graphql-go/graphql
https://vuejs.org/


Ei kommentteja:

Lähetä kommentti

Huomaa: vain tämän blogin jäsen voi lisätä kommentin.