graphql

package module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 28, 2020 License: MIT Imports: 7 Imported by: 0

README

go-grapqhl-client

A simple graphql library to write and send graphql queries as part of go structs using tags and easily send and receive graphql results. This takes advantage of the JSON tags to serialize an object to a graphql query as well as deserialize the response.

QuickStart

Installation

A normal go get should do the trick: go get github.com/shuttl-io/go-graphql-client.

Basics

First, you will need to create a new Graphql Client. This is essentially a factory for creating new requests. The Graphql Client requires a Transport object. The Transport object is what takes the graphql request, sends it to a graphql api, and then returns the response and deserializes the response. For simplicity, we have included a simple HTTP transport that allows you to send the API and set headers if you need it:

package main

import (
    "github.com/shuttl-io/go-graphql-client"
)

func main() {
    client := graphql.NewClient(graphql.NewSimpleHTTPTransport("https://api.example.com/graphql"))
    ...
}

Breaking this down, the graphql.NewClient takes a Transport object and uses that to create and send requests to some API. The graphql.NewSimpleHTTPTransport takes the URL of the api so it knows where to route requests to.

After that you can use a normal golang struct to make a request against your api:

package main

import (
    "github.com/shuttl-io/go-graphql-client"
)

type HeroObject struct {
    Name string `json:"name"`
}

type HeroWithFriend struct {
    Name string `json:"name"`
    Friend []HeroObject `json:"friends"`
}

type GraphQLRequest struct {
    Hero Hero `json:"hero"`
}

func main() {
    client := graphql.NewClient(graphql.NewSimpleHTTPTransport("https://api.example.com/graphql"))
    req := &GraphQLRequest{}
    resp, err := client.NewRequest().Query(req).Send()
    fmt.Print(req.Hero.Name)
}

This will send a request to your graphql api that looks like this:

query {
  hero {
    name
    friends {
      name
    }
  }
}

client.NewRequest() starts a new Request context that you can use to add a query or mutation and variables. You then pass your query struct into .Query() or .Mutation() (depending on what you are trying to do). When the request comes back from the server, it will be automatically unmarshalled into the &GraphQLRequest{} object on line 21. .Send() actually sends the request to the server and will return a Response object that contains the http.Response and http.Request objects as well as the raw bytes of the response in Response.Payload as well as the deserialized object on Request.Response that is just an interface{} which you can then cast to your graphql request object.

Adding params

Now we have the basics, how do we query a graphql api? Its really simple in this library. All you need to do is add a tag to your struct that defines a field as queryable and its params. This tag is the gql_params tag and it takes the following format as a value: "<argument name>:<graphql type>". This tag is comma separated for multiple args After this is done, then calling .WithVariable on the request will format the query correctly:

package main

import (
    "github.com/shuttl-io/go-graphql-client"
)

type HeroObject struct {
    Name string `json:"name"`
}

type HeroWithFriend struct {
    Name string `json:"name"`
    Friend []HeroObject `json:"friends"`
}

type GraphQLRequest struct {
    Hero Hero `json:"hero" gql_params:"id:ID"`
}

func main() {
    client := graphql.NewClient(graphql.NewSimpleHTTPTransport("https://api.example.com/graphql"))
    req := &GraphQLRequest{}
    resp, err := client.NewRequest()
        .Query(req)
        .WithVariable("id", "1000")
        .Send()
    fmt.Print(req.Hero.Name)
}

This will send a query that looks like this:

query($id: ID) {
  hero(id: $id) {
    name
    friends {
      name
    }
  }
}

With the value for ID being sent in the variables argument of the request. If you don't pass an argument via the .WithVariable, this library will simply not format any arguments on to the request. That is, the request will only contain variables that the request was asked to include.

Ignoring a specific field

If you need to ignore a specific field but want it on the query. you can add the tag and value gql:"omit" to the struct. This will not add the field to the query

package main

import (
    "github.com/shuttl-io/go-graphql-client"
)

type HeroObject struct {
    Name string `json:"name"`
    IgnoreField `json:"ignore" gql:"omit"`
}

type HeroWithFriend struct {
    Name string `json:"name"`
    Friend []HeroObject `json:"friends"`
}

type GraphQLRequest struct {
    Hero Hero `json:"hero" gql_params:"id:ID"`
}

func main() {
    client := graphql.NewClient(graphql.NewSimpleHTTPTransport("https://api.example.com/graphql"))
    req := &GraphQLRequest{}
    resp, err := client.NewRequest()
        .Query(req)
        .WithVariable("id", "1000")
        .Send()
    fmt.Print(req.Hero.Name)
}

This will send a query that looks like this:

query($id: ID) {
  hero(id: $id) {
    name
    friends {
      name
    }
  }
}
Aliasing field

Sometimes it is necessary to alias a graphql field. To do that, use the JSON tag and the gql tag at the same time:

package main

import (
    "github.com/shuttl-io/go-graphql-client"
)

type HeroObject struct {
    Name string `json:"hero_name" gql:"name"`
    IgnoreField `json:"ignore" gql:"omit"`
}

type HeroWithFriend struct {
    Name string `json:"name"`
    Friend []HeroObject `json:"friends"`
}

type GraphQLRequest struct {
    Hero Hero `json:"hero" gql_params:"id:ID"`
}

func main() {
    client := graphql.NewClient(graphql.NewSimpleHTTPTransport("https://api.example.com/graphql"))
    req := &GraphQLRequest{}
    resp, err := client.NewRequest()
        .Query(req)
        .WithVariable("id", "1000")
        .Send()
    fmt.Print(req.Hero.Name)
}

This will send a query that looks like this:

query($id: ID) {
  hero(id: $id) {
    hero_name: name
    friends {
      name
    }
  }
}

Full Working and Copy Pastable Code

package main

import (
	"fmt"

	"github.com/shuttl-io/go-graphql-client"
)

type continent struct {
	Code string `json:"code"`
	Name string `json:"name"`
}

type continentsRequest struct {
	Continents []continent `json:"continents" gql_params:"filter:ContinentFilterInput"`
}

type stringQuery struct {
	Eq    string   `json:"eq,omitempty"`
	Ne    string   `json:"ne,omitempty"`
	In    []string `json:"in,omitempty"`
	Nin   string   `json:"nin,omitempty"`
	Regex string   `json:"regex,omitempty"`
	Glob  string   `json:"glob,omitempty"`
}

type ContinentFilter struct {
	Code stringQuery `json:"code"`
}

func main() {
	client := graphql.NewClient(graphql.NewSimpleHTTPTransport("https://countries.trevorblades.com/"))
	req := &continentsRequest{}
	filter := ContinentFilter{}
	filter.Code.Eq = "AF"
	_, err := client.NewRequest().Query(req).WithVariable("filter", filter).Send()
	if err != nil {
		fmt.Println("ERROR:", err)
	}
	for _, continent := range req.Continents {
		fmt.Println("Continent Code:", continent.Code, "Name:", continent.Name)
		fmt.Println("========================================================")
	}
}

Contributions

Contributions are 100% encouraged. This can take many forms. Just using this project is contribution enough. If you run into an issue, please drop a line in the issues and we will get back to you ASAP. If you want to open a PR, that is cool too, just go ahead and open one.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Client

type Client interface {
	// NewRequest makes a new request to send to some graphql response
	NewRequest() Request

	// AddRequestModifier adds a request modifier to the client, use this to add headers or do some retry logic
	SetTransport(transport Transport) Client
}

Client represents a GraphQL client

func NewClient

func NewClient(transport Transport) Client

NewClient returns a new Graphql Client

type GqlMarshaler

type GqlMarshaler interface {
	MarshalGql(marshaler *Marshaler) ([]*QueryPart, error)
}

GqlMarshaler implements a way to Marshal to a graphql request. This returns a list of QueryParts to add to the parent part of the graphql API.

type Marshaler

type Marshaler struct {
	IdentLevel int
	// contains filtered or unexported fields
}

Marshaler marshels the object to a graphql request

func Marshal

func Marshal(obj interface{}) (*Marshaler, error)

Marshal will start the marshalling process and convert the object to a graphql request

func NewMarshaler

func NewMarshaler() *Marshaler

NewMarshaler returns a new Marshaler

func (*Marshaler) AddToArgs

func (g *Marshaler) AddToArgs(argNames ...string)

AddToArgs adds a new arg name to be added as part of the query

func (*Marshaler) MarshalToGraphql

func (g *Marshaler) MarshalToGraphql(obj interface{}, argsToInclude ...string) (string, error)

MarshalToGraphql takes the object and returns a new graphql query

func (*Marshaler) String

func (g *Marshaler) String() string

String converts the current state of the Marshaler to a string

type QueryPart

type QueryPart struct {
	// Value is the name of the field that graphql needs
	Value string
	// Arguments are the arguments that this field can take this maps args to the Graphql type
	Arguments map[string]string
	// Subfield represents other fields, for example in a bigger queries
	SubFields []*QueryPart
	// contains filtered or unexported fields
}

QueryPart Represents a part of a graphql query

func NewQueryPart

func NewQueryPart(name string) *QueryPart

NewQueryPart creates a new QueryPart

func (*QueryPart) String

func (q *QueryPart) String() string

type Request

type Request interface {

	// SetTransport sets the transport for the request when send
	SetTransport(transport Transport) Request

	//WithVariable adds a variable to the request
	WithVariable(name string, value interface{}) Request

	// Query sets the request to a query. It will take the interface and perform reflection to see
	// what fields to request from graphql. The fields should be json serializable (that this
	// Respects the json tags) or you could use the graphql tags for more specific uses
	Query(object interface{}) Request

	// Mutation sets the request to a mutation. It will take the interface and perform reflection to see
	// what fields to request from graphql. The fields should be json serializable (that this
	// Respects the json tags) or you could use the graphql tags for more specific uses
	Mutation(object interface{}) Request

	// Send sends the request to the graphql API. This returns a filled out version of the interface passed
	// into query or mutation methods
	Send() (Response, error)

	// GetQuery gets the full query
	GetQuery() string

	//GetVariables gets the variables for the request
	GetVariables() map[string]interface{}

	//GetInterface gets the interface that the response should adhere to
	GetInterface() interface{}
}

Request represents a graphql request to some API

type Response

type Response struct {
	// HttpResponse gets the header off the response
	HttpResponse *http.Response
	// Payload is the raw payload
	Payload []byte
	// obj is the object to unserialize
	Response interface{}
	// HttpRequest is the raw Request
	HttpRequest *http.Request
}

Response represents a response from a graphql api

type SimpleHTTPTransport

type SimpleHTTPTransport struct {
	// contains filtered or unexported fields
}

SimpleHTTPTransport is a simple http api that allows you to make a single request to headers get the response from the API.

func NewSimpleHTTPTransport

func NewSimpleHTTPTransport(apiURL string) *SimpleHTTPTransport

NewSimpleHTTPTransport takes the api URL and then returns a SimpleHttpTransport

func (*SimpleHTTPTransport) AddHeader

func (s *SimpleHTTPTransport) AddHeader(name string, value string)

AddHeader adds a header onto therequest object

func (*SimpleHTTPTransport) Transport

func (s *SimpleHTTPTransport) Transport(req Request) (Response, error)

Transport the request to the API

type Transport

type Transport interface {
	// ModifyRequest takes a request and then returns the request.
	Transport(req Request) (Response, error)
}

Transport transports the request to an API and returns the response

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL