This is Go library for building GraphQL client with gqlgen

gqlgenc

What is gqlgenc ?

This is Go library for building GraphQL client with gqlgen

Motivation

Now, if you build GraphQL api client for Go, have choice:

These libraries are very simple and easy to handle. However, as I work with gqlgen and graphql-code-generator every day, I find out the beauty of automatic generation. So I want to automatically generate types.

Installation

go get -u github.com/Yamashou/gqlgenc

How to use

Client Codes Only

gqlgenc base is gqlgen with plugins. So the setting is yaml in each format.
gqlgenc can be configured using a .gqlgenc.yml file

Load a schema from a remote server:

model:
  package: generated
  filename: ./models_gen.go # https://github.com/99designs/gqlgen/tree/master/plugin/modelgen
client:
  package: generated
  filename: ./client.go # Where should any generated client go?
models:
  Int:
    model: github.com/99designs/gqlgen/graphql.Int64
  Date:
    model: github.com/99designs/gqlgen/graphql.Time
endpoint:
  url: https://api.annict.com/graphql # Where do you want to send your request?
  headers: # If you need header for getting introspection query, set it
    Authorization: "Bearer ${ANNICT_KEY}" # support environment variables
query:
  - "./query/*.graphql" # Where are all the query files located?

Load a schema from a local file:

model:
  package: generated
  filename: ./models_gen.go # https://github.com/99designs/gqlgen/tree/master/plugin/modelgen
client:
  package: generated
  filename: ./client.go # Where should any generated client go?
models:
  Int:
    model: github.com/99designs/gqlgen/graphql.Int64
  Date:
    model: github.com/99designs/gqlgen/graphql.Time
schema:
  - "schema/**/*.graphql" # Where are all the schema files located?
query:
  - "./query/*.graphql" # Where are all the query files located?

Execute the following command on same directory for .gqlgenc.yml

gqlgenc

With gqlgen

Do this when creating a server and client for Go. You create your own entrypoint for gqlgen. This use case is very useful for testing your server.

package main

import (
	"fmt"
	"os"

	"github.com/Yamashou/gqlgenc/clientgen"

	"github.com/99designs/gqlgen/api"
	"github.com/99designs/gqlgen/codegen/config"
)

func main() {
	cfg, err := config.LoadConfigFromDefaultLocations()
	if err != nil {
		fmt.Fprintln(os.Stderr, "failed to load config", err.Error())
		os.Exit(2)
	}
	queries := []string{"client.query", "fragemt.query"}
	clientPackage := config.PackageConfig{
		Filename: "./client.go",
		Package:  "gen",
	}

	clientPlugin := clientgen.New(queries, clientPackage)
	err = api.Generate(cfg,
		api.AddPlugin(clientPlugin),
	)
	if err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
		os.Exit(3)
	}
}

Documents

Comments

Japanese Comments

These codes have Japanese comments. Replace with English.

Subscription

This client does not support subscription. If you need a subscription, please create an issue or pull request.

Pre-conditions

clientgen is created based on modelgen. So if you don't have a modelgen, it may be a mysterious move.

Comments
  • Fixed issue when the operation has no name

    Fixed issue when the operation has no name

    An "UnamedX" name is used so that generated code compiles correctly.

    I went the easy way to start with this issue, however, I had another idea. What about instead of being unamed, the name is picked from the "operation" file if possible. It could be transformed such that it becomes a valid PascalCase identifier.

    Might be a bit intense and in almost in every cases, it's simply better to give a name to the operation, so the UnameX is most probably a more than good enough fix.

  • feat(interface): generate client interface

    feat(interface): generate client interface

    attempting to address #81 and generate interfaces as well, I'm not familiar with this codebase, I'd like to know if I'm on right track.

    Also what else do I need to do? :)

  • (#51) add

    (#51) add "omitempty" option to nilable fields

    I faced the problem that is described in #51 and after short investigation came up with this solution. please let me know if tests needs to be added, etc.

    closes #51

  • Added possibility to only generate the operations and not the actual client code

    Added possibility to only generate the operations and not the actual client code

    Fixes #45

    Discussion

    What do you think about splitting the clientgen plugin in two halves? An opsgen plugin that would generate just the types and another plugin clientgen that would deal only with the client access code, that will become more important when Subscription will be supported.

    A direct copy should be possible at the expense of a good duplication of certain important structure & use cases. This would require some refactoring IMO to ensure the maximum of stuff can be shared between both plugin.

    That would remove the need for the new option config I added and turn the conditional generation by tweaking the top-level config.

    Maybe a good idea for the v2 work since it requires a fair amount of changes to merge it efficiently.

  • add map support for json parser

    add map support for json parser

    Currently, there is no support for a schema with map[string]interface{}. This PR extends the decode method to unmarshal the map type.

    It was the main reason why I opened the previous issue: #141 All tests are passing right now

  • Error: cannot use config.PackageConfig{...} (type config.PackageConfig) as type config.ExecConfig in field value

    Error: cannot use config.PackageConfig{...} (type config.PackageConfig) as type config.ExecConfig in field value

    This error occured in the latest version: v0.0.2: ../../../../go/pkg/mod/github.com/!yamashou/[email protected]/config/config.go:203:3: cannot use config.PackageConfig{...} (type config.PackageConfig) as type config.ExecConfig in field value

  • Unmarshal graphql response with json stdlib

    Unmarshal graphql response with json stdlib

    First of all, thank the author for providing this tool that can generate graphql client. But we also encountered some problems during use.

    Similar to the situation in #67, a scalar is defined in our schema, and the return value of the server is a map with an unfixed structure.

    We first specify the type to which scalar is mapped in the following way:

    models:
      Object:
        model: github.com/99designs/gqlgen/graphql.Map
    

    However, in the response of the parsing server, we got the following error:

    Error failed to decode data into response ... struct field for "xxx" doesn't exist in any of 1 places to unmarshal
    

    By reading the code, we found that gqlgenc will parse the response content through the graphqljson package, and always make judgments based on the way the target type is a structure.

    However, on the other hand, we also found that in the generated code, each field of the corresponding structure already has a corresponding json tag. We can directly use the json standard library for unmarshal.

    type LanguageFragment struct {
    	ID   string "json:\"id\" graphql:\"id\""
    	Name string "json:\"name\" graphql:\"name\""
    }
    

    So, can we use json instead to parse the response?

  • Added support to unmarshal json.RawMessage fields

    Added support to unmarshal json.RawMessage fields

    Problem

    Our graphql schema contains json scalars. It's mainly used for fields that require a bit more flexibility. Currently, the only way to use gqlgenc with json scalars is to pretend that all json scalars map to a specific struct and write a un/marshaller for that type. It works ok if you have 1 json scalar in your schema, but that's not our case.

    A better way of approaching that issue would be to use the json.RawMessage type. That way, you are pushing the unmarshalling logic down the consumer of the gqlgenc client. Unfortunately, this solution wasn't supported out of the box because the graphql decoder in graphqljson/graphql.go didn't handle the json.RawMessage type and it would result in an error.

    Solution

    Modify the decode function of the Decoder to handle the json.RawMessage type.

    Consideration

    In the decode function, when the code is at a point where it's in an object, read the key and found a matching field in the referred struct, the tie breaker for the matchingFieldValue is the position in the stacks (last matching one in d.vs). It's to reflect more or less what's happening when the value is actually processed (line 189-198).

  • Conditional fragments (on sub-types of a union or an interface) should be represented as pointers

    Conditional fragments (on sub-types of a union or an interface) should be represented as pointers

    A Fragment is only applied on its type. It's therefor only applicable if the payload contains the data for that type.

    However fragments are currently represented as non-indirect (non-pointer) types in the generated client code (both v1 & v2). This leads all fragments subfields to be populated with default empty data in the JSON unmarshalling.

    If fragments were represented as pointers it would be possible to do a nil-check on the root fragment field to check types. As is, there's no good way to check if fragments are present.

    Consider a query using two fragments as such:

    query ConversationByIDQuery($id: ID!) {
        conversation(id: $id) {
            messages {
                ...answerFragment
                ...questionFragment
            }
        }
    }
    
    fragment answerFragment on ChatAnswerMessage {
        answer {
            ... on ChatSingleSelectAnswer {
                selectionId
            }
        }
    
    }
    
    fragment questionFragment on ChatQuestionMessage {
        question {
            ... on ChatSingleSelectQuestion {
                options {
                    id
                    text
                }
            }
        }
    }
    

    Furthermore, consider a schema in which the types referenced by the fragment are mutually exclusive. Perhaps two types implementing the same interface:

    query {
        conversation(id: ID!): Conversation
    }
    
    type Conversation {
      id: ID!
      messages: [ChatMessage!]!
    }
    type ChatAnswerMessage implements ChatMessage {
      answer: ChatSingleSelectAnswer!
    }
    type ChatSingleSelectAnswer {
      selectionId: ID!
    }
    type ChatQuestionMessage implements ChatMessage, ChatMessageWithText, ChatMessageWithUser {
      question: ChatSingleSelectQuestion,!
    }
    type ChatSingleSelectQuestion {
      options: [ChatSelectOption!]!
    }
    type ChatSelectOption {
      id: ID!
      text: String!
    }
    

    In this case the generated client payload does not allow you to distinguish which of the two types are represented.

    type ConversationByIDQuery struct {
    	Conversation *struct {
    		Messages []*struct {
    			Answer struct {
    				SelectionID string "json:\"selectionId\" graphql:\"selectionId\""
    			} "json:\"answer\" graphql:\"answer\""
    			Question struct {
    				Options []*struct {
    					ID   string "json:\"id\" graphql:\"id\""
    					Text string "json:\"text\" graphql:\"text\""
    				} "json:\"options\" graphql:\"options\""
    			} "json:\"question\" graphql:\"question\""
    		} "json:\"messages\" graphql:\"messages\""
    	} "json:\"conversation\" graphql:\"conversation\""
    }
    

    In most of the examples in the example directory here, this issue isn't visible because the fragments are applied directly to non-required (Type, not Type!) or list types (with the default config omit_slice_element_pointers: false).

  • use standard json.Unmarshal instead of custom

    use standard json.Unmarshal instead of custom

    I have a very complex response from the API and the graphqljson.UnmarshalData throws errors. After changing to standard json.Unmarshal everything works fine.

  • prevent overwrite Header by HTTPRequestOption function

    prevent overwrite Header by HTTPRequestOption function

    Fixes: https://github.com/Yamashou/gqlgenc/issues/109

    This PR prevents the situation when the Header "Content-Type" for type HTTPRequestOption func(req *http.Request) function is overwritten. I have implemented my own HTTPRequestOption function where I set it for multipart/form-data; boundary= to be able to upload files but finally, the req Header was different.

  • Invalid client: expected declaration, found this

    Invalid client: expected declaration, found this

    This is the error generated and it also generates invalid client code.

    // Code generated by github.com/Yamashou/gqlgenc, DO NOT EDIT.
    
    package sorql
    
    import (
    )
    this is my test package
    
    gofmt failed on gen_client.go: /Users/staylor279/code/bosun/server/gql/sorql/gen_client.go:7:1: expected declaration, found this
    

    version: github.com/Yamashou/gqlgenc v0.11.1

    GO111MODULE="on"
    GOARCH="amd64"
    GOBIN="/usr/local/go/bin"
    GOCACHE="/Users/staylor279/Library/Caches/go-build"
    GOENV="/Users/staylor279/Library/Application Support/go/env"
    GOEXE=""
    GOEXPERIMENT=""
    GOFLAGS=""
    GOHOSTARCH="amd64"
    GOHOSTOS="darwin"
    GOINSECURE=""
    GOMODCACHE="/Users/staylor279/go/pkg/mod"
    GONOPROXY=""
    GONOSUMDB=""
    GOOS="darwin"
    GOPATH="/Users/staylor279/go"
    GOPRIVATE=""
    GOPROXY="https://proxy.golang.org,direct"
    GOROOT="/usr/local/go"
    GOSUMDB="sum.golang.org"
    GOTMPDIR=""
    GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"
    GOVCS=""
    GOVERSION="go1.19.4"
    GCCGO="gccgo"
    GOAMD64="v1"
    AR="ar"
    CC="clang"
    CXX="clang++"
    CGO_ENABLED="1"
    GOMOD="/Users/staylor279/code/bosun/go.mod"
    GOWORK=""
    CGO_CFLAGS="-g -O2"
    CGO_CPPFLAGS=""
    CGO_CXXFLAGS="-g -O2"
    CGO_FFLAGS="-g -O2"
    CGO_LDFLAGS="-g -O2"
    PKG_CONFIG="pkg-config"
    GOGCCFLAGS="-fPIC -arch x86_64 -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/f_/jnl41d395bvdtk45cxfd5hm40000gq/T/go-build868593632=/tmp/go-build -gno-record-gcc-switches -fno-common"
    
    
  • erorr processing `type Query`

    erorr processing `type Query`

    Hello, I have queries defined as

    type Query {
      query1(): boolean
      query2(input: MyInput!): MyOutput!
    }
    

    as per https://www.apollographql.com/docs/apollo-server/schema/schema/#the-query-type:

    The Query type is a special object type that defines all of the top-level entry points for queries that clients execute against your server.

    same for type Mutation. Seems that gqlgenc is unable to process such files when generating operations: Unexpected token Name "type"

    Should it work, or what am I doing wrong?

    gqlgen generates server code well from those files.

  • Error handling is quite verbose

    Error handling is quite verbose

    I would like to be able to check if there are network errors separate to graphql errors, and maybe log these as part of the generated client.

    Having to do the following seems like a lot of code to check if an entity didn't exist. I have User in my schema, not User!.

    Is this a gqlgenc issue or have I set up gqlgen incorrectly?

    if err != nil {
    	if handledError, ok := err.(*client2.ErrorResponse); ok {
    		if (*handledError.GqlErrors)[0].Message == sql.ErrNoRows.Error() {
    			c.JSON(http.StatusUnauthorized, gin.H{
    				"error": err.Error(),
    			})
    			return
    		}
    	}
    
    	log.Println(err)
    }
    
  • Customise GraphQL response with extensions

    Customise GraphQL response with extensions

    Currently I am using the PayPal / Braintree GraphQL endpoint: https://graphql.braintreepayments.com

    And it generates responses such as this:

    {
      "data": {
        "createClientToken": {
          "clientMutationId": null,
          "clientToken": "LCJhdXRob3JpemF0aW9uRmluZ2VycHJpbnQiOiJleUowZVhBaU9pSktWMVFpTENKaGJHY2lPaUpGVXpJMU5pSXNJbXRwWkNJNklqSXdNVGd3TkRJMk1UWXRjMkZ1WJNkltaDBkSEJ6T2k4dllYQnBMbk5oYm1SaWIzZ3VZbkpoYVc1MGNtVmxaMkYwWlhkaGVTNWpiMjBpZlEuZXlKbGVIQWlPakUyTmpjek1qQXhPVFlzSW1wMGFTSTZJ..."
        }
      },
      "extensions": {
        "requestId": "42629c2f-3d6e-573g-ae48-0c69410ab452"
      }
    }
    

    but it seems the client only parses the data portion of the response. ( and errors if they exist )

    Is there any way to fix this ?

  • Environment variables not working

    Environment variables not working

    I followed README instructions to setup the gqlgenc.yml file, specifically using env variables as explained:

    endpoint:
      url: https://api.annict.com/graphql # Where do you want to send your request?
      headers: # If you need header for getting introspection query, set it
        Authorization: "Bearer ${ANNICT_KEY}" # support environment variables
    

    Here's my configuration:

    ...
    endpoint:
      url: ${HASURA_GRAPHQL_ENDPOINT}
        headers: # If you need header for getting introspection query, set it
          Content-Type: "application/json"
          X-Hasura-Admin-Secret: ${HASURA_GRAPHQL_ADMIN_SECRET}
    ...
    

    but it doesn't work. I'm sure the endpoint works because if I hardcode those values everything goes well.

    Am I missing something?

The easiest way to make API documents for GraphQL

Document Generator for GraphQL gqldoc is now alpha gqldoc is command line tool to generate documents from GraphQL schema or your GraphQL endpoint. the

Dec 20, 2022
A port of the parser from graphql-js into golang

gqlparser This is a parser for graphql, written to mirror the graphql-js reference implementation as closely while remaining idiomatic and easy to use

Dec 27, 2022
gqlanalysis makes easy to develop static analysis tools for GraphQL in Go.
gqlanalysis makes easy to develop static analysis tools for GraphQL in Go.

gqlanalysis gqlanalysis defines the interface between a modular static analysis for GraphQL in Go. gqlanalysis is inspired by go/analysis. gqlanalysis

Dec 14, 2022
Minimal UART client in Golang that dumps LPC1343 chips that are locked at CRP1.

Howdy y'all, This is a quick and dirty client for the UART bootloader of the LPC1343, and probably other bootloaders in that chip family. This client

Dec 2, 2022
This project contains an example that showcases different features from the official Go Client for Elasticsearch
This project contains an example that showcases different features from the official Go Client for Elasticsearch

Elasticsearch for Gophers This project contains an example that showcases different features from the official Go Client for Elasticsearch that you ca

Oct 12, 2022
Utility functions for work with the Kubernetes Go-Client

go-k8s-utils This repository contains utils for the work with Kubernetes, in specific with the go-client library. Testing This package contains utils

Dec 14, 2022
GoLang-based client-side circuit breakers and helpers

Overview Example library for circuit breaking in GoLang. Written to support a blog post on https://www.wojno.com. Use this library in your SDK's to pr

Dec 5, 2021
Drone eReg: Demo client application for the PKI server's built-in UAV registry

UAV e-Registration: Demo UAV Registry Client A client to register UAVs in the built-in demo UAV registry of the UAVreg-PKI-server. Installation and Us

Jan 5, 2022
Govalid is a data validation library that can validate most data types supported by golang

Govalid is a data validation library that can validate most data types supported by golang. Custom validators can be used where the supplied ones are not enough.

Apr 22, 2022
Maintain a lower-bitrate copy of a music library in sync with the main copy.

msync Maintain a lower-bitrate copy of your music library, in sync with the main copy.

Mar 6, 2022
Golang library to act on structure fields at runtime. Similar to Python getattr(), setattr(), hasattr() APIs.

go-attr Golang library to act on structure fields at runtime. Similar to Python getattr(), setattr(), hasattr() APIs. This package provides user frien

Dec 16, 2022
Go library for HTTP content type negotiation

Content-Type support library for Go This library can be used to parse the value Content-Type header (if one is present) and select an acceptable media

Jul 10, 2022
A tool and library for using structural regular expressions.

Structural Regular Expressions sregx is a package and tool for using structural regular expressions as described by Rob Pike (link).

Dec 7, 2022
A super simple Lodash like utility library with essential functions that empowers the development in Go
A super simple Lodash like utility library with essential functions that empowers the development in Go

A simple Utility library for Go Go does not provide many essential built in functions when it comes to the data structure such as slice and map. This

Jan 4, 2023
go-sysinfo is a library for collecting system information.

go-sysinfo go-sysinfo is a library for collecting system information. This includes information about the host machine and processes running on the ho

Dec 26, 2022
Molecule is a Go library for parsing protobufs in an efficient and zero-allocation manner

Molecule Molecule is a Go library for parsing protobufs in an efficient and zero-allocation manner. The API is loosely based on this excellent Go JSON

Jan 5, 2023
A Go (golang) library for parsing and verifying versions and version constraints.

go-version is a library for parsing versions and version constraints, and verifying versions against a set of constraints. go-version can sort a collection of versions properly, handles prerelease/beta versions, can increment versions, etc.

Jan 9, 2023
A library for parsing ANSI encoded strings
 A library for parsing ANSI encoded strings

Go ANSI Parser converts strings with ANSI escape codes into a slice of structs that represent styled text

Sep 20, 2022
A library for diffing golang structures

Diff A library for diffing golang structures and values. Utilizing field tags and reflection, it is able to compare two structures of the same type an

Dec 29, 2022