garphunql

package module
v0.0.4 Latest Latest
Warning

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

Go to latest
Published: Jan 20, 2020 License: MIT Imports: 11 Imported by: 1

README

Garphunql Build Status Coverage Status

Garphunql is a Golang client library for GraphQL. It supports building queries in a type-safe way, then submitting them to the server and automatically unmarshaling the responses.

Usage

Getting a Client

Garphunql's functionality is provided as methods on a Client object. You instantiate a client with the NewClient function, providing the server URL and any number of headers. Here's an example of making a client to talk to the Github GraphQL API. (In all the examples here, garphunql has been imported with the gql alias.):

package main

import (
  "fmt"

  gql "github.com/btubbs/garphunql"
)

func main() {
	client := gql.NewClient(
		"https://api.github.com/graphql",
		gql.Header("Authorization", "bearer aidee6gahPe1baeth8tikeijeeth0aedaehe"),
	)
  // ...
}
Making a simple query

Once you have a client you can query for data using the client's Query method. The Query method takes a field, which you can construct with the Field function, passing it a name, the sub-fields you want to get back, and a destination that the results should be unmarshaled into:

type User struct {
	Name     string `json:"name"`
	Location string `json:"location"`
}

func simpleQuery(client *gql.Client) {
	var me User
	meField := gql.Field("viewer",
		gql.Field("name"),
		gql.Field("location"),
		gql.Dest(&me),
	)
	err := client.Query(meField)
	fmt.Println(err, me)
}
Passing arguments to fields

Some GraphQL fields take arguments. Garphunql supports querying those fields by passing in one or more Arg calls to Field. Here we query Github for another user, passing in a value for the login argument:

func queryWithArguments(client *gql.Client) {
	var zach User
	zachField := gql.Field("user",
		gql.Arg("login", "zachabrahams"),
		gql.Field("name"),
		gql.Field("location"),
		gql.Dest(&zach),
	)
	err := client.Query(zachField)
	fmt.Println(err, zach)
}
More deeply nested fields

GraphQL can have fields within fields within fields, etc. This example shows a label field nested inside a permissions field nested inside a licenses field (which is also automatically nested inside a query field before Garphunql sends it over the wire):

func deeplyNestedFields(client *gql.Client) {
	var licenses []License
	licensesField := gql.Field("licenses",
		gql.Field("name"),
		gql.Field("permissions",
      gql.Field("label"),
    ),
	)
	err := client.Query(
		licensesField(gql.Dest(&licenses)),
	)
	fmt.Println(err, licenses)
}
Querying multiple fields at once

GraphQL lets you query any number of fields at the same time. Similarly, Garphunql lets you pass in any number of Field calls to Query. The fields will all be bundled together and sent to the server as sub-fields of the top-level "query" field. When the server's response is received, each piece of the payload will be unmarshaled into the appropriate destination:

func multipleQueries(client *gql.Client) {
	var me User
	var zach User
	meField := gql.Field("viewer",
		gql.Field("name"),
		gql.Field("location"),
		gql.Dest(&me),
	)
	zachField := gql.Field("user",
		gql.Arg("login", "zachabrahams"),
		gql.Field("name"),
		gql.Field("location"),
		gql.Dest(&zach),
	)
	err := client.Query(meField, zachField)
	fmt.Println(err, me, zach)
}
Re-using fields with late binding

You might want to query the same field multiple times with different arguments. Garphunql lets you partially define a field with the options shared between your calls, then call it later with more arguments to customize it:


func lateBoundFields(client *gql.Client) {
	var pedro User
	var sean User
	unboundUserField := gql.Field("user",
		gql.Field("name"),
		gql.Field("location"),
	)
	err := client.Query(
		unboundUserField(gql.Arg("login", "steenzout"), gql.Dest(&pedro)),
		unboundUserField(gql.Arg("login", "sophisticasean"), gql.Dest(&sean)),
	)
	fmt.Println(err, pedro, sean)
}

There's one more thing to note about that example. We made two queries to the user field. Normally GraphQL would require you to provide an alias for at least one of them so that they could be differentiated in the response payload. Garphunql automatically detected the name collision while building the query and set an alias for the second user field behind the scenes.

Handling resolver errors

In a GraphQL server, every field is populated by a "resolver". Because you can query multiple fields at once, it's possible for some of their resolvers to succeed, while others fail. In this case, the server will return null for the failed fields, and add objects to the "errors" element of the response payload. Garphunql bundles those errors into a multierror and returns it as the result of the Query call, while still populating the destination variables of the fields that succeeded:

func errorHandling(client *gql.Client) {
	var me User
	var nobody User
	meField := gql.Field("viewer",
		gql.Field("name"),
		gql.Field("location"),
		gql.Dest(&me),
	)
	userField := gql.Field("user",
		gql.Arg("login", "TOTALLY FAKE USER"),
		gql.Field("name"),
		gql.Field("location"),
		gql.Dest(&nobody),
	)
	err := client.Query(
		meField,
		userField,
	)
	fmt.Println(err, me, nobody)
}

The function above will print the error returned by Github (* Could not resolve to a User with the login of 'TOTALLY FAKE USER'.), as well as printing the populated me and the empty nobody.

Mutations

In addition to the Query method, the Garphunql client provides a Mutation method. It works identically, except that you may only provide a single top-level field.

TODO

  • request variables
  • input objects
  • ... on syntax

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Alias

type Alias string

func (Alias) UpdateField

func (a Alias) UpdateField(f GraphQLField) GraphQLField

type Argument

type Argument struct {
	Name  string
	Value interface{}
}

An Argument represents a GraphQL argument, containing a name and value.

func Arg

func Arg(name string, value interface{}) Argument

Arg is a shorthand for building an Argument.

func (Argument) UpdateField

func (a Argument) UpdateField(f GraphQLField) GraphQLField

type ArgumentFormatter

type ArgumentFormatter interface {
	Format() (string, error)
}

ArgumentFormatter - interface for a format method

type Client

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

Client is the object used for making all requests.

func NewClient

func NewClient(url string, options ...ClientOption) *Client

NewClient returns a new client object.

func (*Client) Mutation

func (c *Client) Mutation(f Fielder) error

Mutation accepts a GraphQLField, wraps it in a "mutation" field, performs the query, then scans the result into the field's dest.

func (*Client) MutationContext added in v0.0.4

func (c *Client) MutationContext(ctx context.Context, f Fielder) error

func (*Client) Query

func (c *Client) Query(first Fielder, more ...Fielder) error

Query takes one or more GraphQLField objects. It joins all the fields into a single query, sends it to the server, and unmarshals the results into Dest fields of the GraphQLField objects.

func (*Client) QueryContext added in v0.0.4

func (c *Client) QueryContext(ctx context.Context, first Fielder, more ...Fielder) error

func (*Client) RawRequest

func (c *Client) RawRequest(ctx context.Context, query string) ([]byte, error)

RawRequest takes a byte slice with your graphQL query inside it, and returns a byte slice with the graphql response inside it, or an error.

func (*Client) Request

func (c *Client) Request(ctx context.Context, f Fielder, out interface{}) error

Request takes one GraphQLField object and the object or pointer that you want to have the results unmarshaled into. It then does the request and unmarshals the result for you.

type ClientOption

type ClientOption func(*Client)

A ClientOption is a function that modifies a *Client.

func Header(key, val string) ClientOption

func HttpClient added in v0.0.4

func HttpClient(httpClient *http.Client) ClientOption

type DestSetter

type DestSetter func(GraphQLField) GraphQLField

The DestSetter type exists only so we can stick a UpdateField method on it.

func Dest

func Dest(d interface{}) DestSetter

Dest is a helper function for making a DestSetter. Call it with a pointer to a thing as the argument, and use the return value as an argument to Query or Mutation.

func (DestSetter) UpdateField

func (d DestSetter) UpdateField(f GraphQLField) GraphQLField

UpdateField sets the Dest field on a GraphQLField.

type Enum

type Enum string

Enum is a string type alias for argument values that shouldn't have quotes put around them.

func (Enum) Format

func (e Enum) Format() (string, error)

Format - type for Enum

type FieldFunc

type FieldFunc func(...FieldOption) GraphQLField

A FieldFunc can be Render-ed directly, or called with any number of Arguments and GraphQLFields to return a new Render-able GraphQLField.

func Field

func Field(name string, outerOptions ...FieldOption) FieldFunc

Field is a shorthand for building a GraphQLField. It accepts a name, and any number of Arguments and GraphQLFields. It returns a FieldFunc that can be Render-ed directly, or called with more Arguments and GraphQLFields to return a Render-able GraphQLField.

func (FieldFunc) Field

func (f FieldFunc) Field() GraphQLField

Field executes the FieldFunc with no arguments and returns its resulting Field.

func (FieldFunc) GetKey

func (f FieldFunc) GetKey() string

GetKey executes the FieldFunc and then returns the result of calling its GetKey method.

func (FieldFunc) Render

func (f FieldFunc) Render(indents ...bool) (string, error)

Render takes a variable number of bools that indicate the number of indents to use in the query (their value doesn't matter), and returns the rendered field, or an error.

func (FieldFunc) UpdateField

func (f FieldFunc) UpdateField(parent GraphQLField) GraphQLField

UpdateField makes FieldFunc satisfy the FieldOption interface, so FieldFuncs can plug themselves into parents as sub selections.

type FieldOption

type FieldOption interface {
	UpdateField(GraphQLField) GraphQLField
}

type Fielder

type Fielder interface {
	Render(...bool) (string, error)
	GetKey() string
	Field() GraphQLField
}

Fielder defines the functions that a thing must implement in order to be passed to our Query and Mutation functions.

type GenericResult

type GenericResult struct {
	Data   map[string]json.RawMessage `json:"data"`
	Errors []GraphQLError             `json:"errors"`
}

GenericResult matches the outermost structure of a GraphQL response payload.

type GraphQLError

type GraphQLError struct {
	Message    string                 `json:"message"`
	Locations  []GraphQLErrorLocation `json:"locations"`
	Path       []string               `json:"path"`
	Extensions JSONMap                `json:"extensions"`
}

A GraphQLError represents an object returned in the "errors" list in a GraphQL response payload. It also implements the Go error interface.

func (GraphQLError) Error

func (e GraphQLError) Error() string

Error renders the GraphQLError as a single string.

type GraphQLErrorLocation

type GraphQLErrorLocation struct {
	Line   int `json:"line"`
	Column int `json:"column"`
}

GraphQLErrorLocation is a sub-field of GraphQLError.

type GraphQLField

type GraphQLField struct {
	Name      string
	Arguments map[string]interface{}
	Fields    []GraphQLField
	Alias     Alias
	Dest      interface{}
}

GraphQLField is a graphQL field. Normally you will have these built for you by passing arguments to FieldFuncs instead of constructing them directly.

func (GraphQLField) Field

func (f GraphQLField) Field() GraphQLField

Field returns the field's field.

func (GraphQLField) GetKey

func (f GraphQLField) GetKey() string

GetKey returns the field's alias, if it has one, or otherwise its name.

func (GraphQLField) Render

func (f GraphQLField) Render(indents ...bool) (string, error)

Render turns a Field into bytes that you can send in a network request.

func (GraphQLField) UpdateField

func (f GraphQLField) UpdateField(parent GraphQLField) GraphQLField

UpdateField makes GraphQLField satisfy the FieldOption interface, which lets it plug itself into a parent field as a sub selection.

type JSONMap

type JSONMap map[string]interface{}

JSONMap correctly formats objects that are json types

func (JSONMap) Format

func (e JSONMap) Format() (string, error)

Format - type for JSONMap

Jump to

Keyboard shortcuts

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