Git Product home page Git Product logo

gqlc's Introduction

GoDoc Go Report Card build codecov

GraphQL Compiler

gqlc is a compiler for the GraphQL IDL, as defined by the GraphQL spec. Current spec implementation: Current Working Draft

Table of Contents

Getting Started

This section gives a brief intro to using gqlc. For more information, check the docs at gqlc.dev.

Installing

You can either git clone this repo and build from source or download one of the prebuilt releases.

Compiling Your First Schema

To begin, lets use an abbreviated version of the schema used in the examples at graphql.org:

schema {
  query: Query,
  mutation: Mutation
}

type Query {
  "hero returns a character in an episode"
  hero(episode: Episode): Character
}

type Mutation {
  """
  addCharacter adds a new Character given their name and the episodes they appeared in.
  """
  addCharacter(name: String!, episodes: [Episode!]!): Character
}

"Episode represents the episodes of the Star Wars saga."
enum Episode {
  NEWHOPE
  EMPIRE
  JEDI
}

"Character represents a character in any episode of Star Wars."
type Character {
  name: String!
  appearsIn: [Episode]!
}

Now, that we have the schema for our GraphQL service it's time to start implementing it. Typically, when implementing a GraphQL service you're thinking in terms of the IDL, but not writing in it; instead, you're writing in whatever language you have chosen to implement your service in. This is where gqlc comes in handy, by providing you with a tool that can "compile", or translate, your IDL definitions into source code definitions. To accomplish this, simply type the following into your shell:

gqlc --js_out . --doc_out . schema.gql

gqlc will then generate two files:

schema.js: The js generator will output Javascript types for the schema.

var {
  GraphQLSchema,
  GraphQLObjectType,
  GraphQLEnumType,
  GraphQLList,
  GraphQLNonNull,
  GraphQLString
} = require('graphql');

var Schema = new GraphQLSchema({
  query: Query,
  mutation: Mutation
});

var EpisodeType = new GraphQLEnumType({
  name: 'Episode',
  values: {
    NEWHOPE: {
      value: 'NEWHOPE'
    },
    EMPIRE: {
      value: 'EMPIRE'
    },
    JEDI: {
      value: 'JEDI'
    }
  }
});

var QueryType = new GraphQLObjectType({
  name: 'Query',
  fields: {
    hero: {
      type: Character,
      args: {
        episode: {
          type: Episode
        }
      },
      resolve() { /* TODO */ }
    }
  }
});

var MutationType = new GraphQLObjectType({
  name: 'Mutation',
  fields: {
    addCharacter: {
      type: Character,
      args: {
        name: {
          type: new GraphQLNonNull(GraphQLString)
        },
        episodes: {
          type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(Episode)))
        }
      },
      resolve() { /* TODO */ }
    }
  }
});

var CharacterType = new GraphQLObjectType({
  name: 'Character',
  fields: {
    name: {
      type: new GraphQLNonNull(GraphQLString),
      resolve() { /* TODO */ }
    },
    appearsIn: {
      type: new GraphQLNonNull(new GraphQLList(Episode)),
      resolve() { /* TODO */ }
    }
  }
});

schema.md: The doc generator will output CommonMark documentation for the schema.

# Documentation
*This was generated by gqlc.*

## Table of Contents
- [Schema](#Schema)
- [Objects](#Objects)
	* [Character](#Character)
	* [Mutation](#Mutation)
	* [Query](#Query)
- [Enums](#Enums)
	* [Episode](#Episode)

## Schema

*Root Operations*:
- query **([Query](#Query))**
- mutation **([Mutation](#Mutation))**

## Objects

### Character
Character represents a character in any episode of Star Wars.

*Fields*:
- name **(String!)**
- appearsIn **([[Episode](#Episode)]!)**

### Mutation

*Fields*:
- addCharacter **([Character](#Character))**

	  addCharacter adds a new Character
	*Args*:
	- name **(String!)**
	- episodes **([[Episode](#Episode)!]!)**

### Query

*Fields*:
- hero **([Character](#Character))**

	hero returns a character in an episode

	*Args*:
	- episode **([Episode](#Episode))**

## Enums

### Episode
Episode represents the episodes of the Star Wars saga.

*Values*:
- NEWHOPE
- EMPIRE
- JEDI

Now, all you have to do is fill in the resolvers and plug it into an http endpoint. All generators and the compiler, itself, support options to tweak the output.

Supported Languages

The currently supported languages by gqlc for generation are:

Contributing

Thank you for wanting to help keep this project awesome!

Before diving right in, here are a few things to help your contribution be accepted:

Guidelines

When making any sort of contribution remember to follow the Contribution guidelines.

Code Generators

Not every language can be supported directly, so please first create an issue and discuss adding support for your language there. If the community shows enough consensus that your language should be directly supported, then a @gqlc team member will initialize the repository for it and work can commence on implementing it.

If your desired language doesn't show enough support from the community to deem direct support in gqlc, then implementing a plugin is highly encouraged. Check out the plugin docs for more information on how plugins are expected to behave when interacting with gqlc.

gqlc's People

Contributors

zaba505 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

gqlc's Issues

Automatically impl interface fields

If an object implements an interface the user shouldn't have to explicitly add the interface fields to the object. Currently, the implementation mandates this:

interface Iterator {
  next: String
}

type Sentence implements Iterator {
  # next field must be specified
  next: String

  # Now Sentence specific fields
  verbs: [String]
}

The desired usage would look like this:

interface Iterator {
  next: String
}

type Sentence implements Iterator {
  # next field is implied
  # Thus, you can just have sentence specific fields
  verbs: [String]
}

perf(convert): maximize convert read

The type converter only converts one type at a time and doesn’t check to see if the read slice could happen more. It should still convert one type at a time but it should continue converting till it overflows the given read slice or fills it perfectly.

This will both maximize the convert buffer usage and the cases where ioutil.ReadAll is used on the converter since it uses a large read slice.

feat(cmd): add schema generator

Once running services are included as a source of input, one nice feature would be to generate their IDL file if they don't already have one. An example usage would look like: gqlc --schema_out . https://api.example.com/graphql. This would generate a IDL file called schema.gql.

feat(fetch): supply custom headers

Many services require an auth token within the request header. Thus, it is necessary to support at least an auth header flag option when import/fetching types from a remote service.

Two great motivators for this are the Github and Gitlab APIs. Both require bearer tokens in the auth header.

feat(version): add commit prefix to dev version

Currently, it's nearly impossible to track from which commit a dev version of the compiler was built. It would be great if the commit prefix could added to dev version like:
gqlc version gqlcD.E.V-fe193b1 linux/amd64.

Only one challenge with this: How to get commit prefix without introducing more build steps i.e. go build should still be all it takes to build a dev version of the compiler from any branch.

feat(cli): custom enum value directive

Currently, the default value for any enum is just a then enum value stringified. What would be better is a custom directive that would allow a user to provide a value for the enum

feat(docs): add homepage

Don't navigate directly to the guide. First, provide a simple homepage to quickly introduce gqlc and some of its features/use cases.

Integration tests with GraphQL runtimes

A nice CI job(s) would be testing that the generated code compiles at least and possibly provide default resolvers so an e2e test of each runtime could be performed.

resolver func survival

I have a suggestion for the code generator w.r.t resolve functions that survive regeneration.

Given the currently generated code:

var MutationType = graphql.NewObject(graphql.ObjectConfig{
	Name: "Mutation",
	Fields: graphql.Fields{
		"createQuotation": &graphql.Field{
			Type: graphql.NewNonNull(QuotationType),
			Args: graphql.FieldConfigArgument{
				"input": &graphql.ArgumentConfig{
					Type: CreateQuotationInputType,
				},
			},
			Resolve: func(p graphql.ResolveParams) (interface{}, error) { return nil, nil }, // TODO
		},

When the Resolve: snippet is replaced to:

var MutationType = graphql.NewObject(graphql.ObjectConfig{
	Name: "Mutation",
	Fields: graphql.Fields{
		"createQuotation": &graphql.Field{
			Type: graphql.NewNonNull(QuotationType),
			Args: graphql.FieldConfigArgument{
				"input": &graphql.ArgumentConfig{
					Type: CreateQuotationInputType,
				},
			},
			Resolve: resolveMutationCreateQuotation
		},

Then we could have a separate Go source file for each definition which only gets generated if there is no such file.

resolveMutationCreateQuotation.go

func resolveMutationCreateQuotationp graphql.ResolveParams) (interface{}, error) { 
     return nil, nil // TODO 
}

Dart Generator

Is your feature request related to a problem? Please describe.
Can't generate Dart code

Describe the solution you'd like
Generation of Dart code

Enum as default value isn't generated correctly

When a enum value is used as a default value the generated code is incorrect. The Enum name either has to be a string or instead the value given to @as(value: String) needs to be used instead.

Java Generator

This issue is a placeholder for community voting on whether Java should be support by gqlc. If you're in favor of adding a Java generator to gqlc then comment with a +1.

generated union type has wrong Resolve function signature

Given

type A {
    id: ID! 
}

type B {
    id: ID! 
}

type C {
    id: ID! 
}

union U =
    A
    | B
    | C

type Mutation {
    foo : U!
}

and compiling this will generate

... stuff deleted ...

var UType = graphql.NewUnion(graphql.UnionConfig{
	Name: "U",
	Types: []*graphql.Object{
		AType,
		BType,
		CType,
	},
	ResolveType: func(p graphql.ResolveParams) *graphql.Object { return nil }, // TODO
})

which has an incorrect definition for ResolveType.
It must be:

ResolveType: func(p graphql.ResolveTypeParams) *graphql.Object { return nil }, // TODO

Fetch types from running GraphQL services

Description

It would be cool/useful if you import types from an existing service. This could be especially useful for early adoption since most developers don't necessarily write their entire schemas in the GraphQL IDL.

Implementation

Since gqlc already supports remote file importing just one extra step needs to be added. If the endpoint isn't a file send an introspection query for all of its types. An optimization could be included to check if the endpoint ends with /graphql since this is a common naming practice for a GraphQL endpoint.

Overwrite old generated files

An issue arises when you remove things from your .gql source and then try to regenerate your code. The old generated file isn't fully cleared which leads to garbling of text.

Work with non-local files e.g. http or ftp

Is your feature request related to a problem? Please describe.
Currently, in order to process a remote file you have to download it locally and then pass it to gqlc. This isn't necessary since most use cases of the *.gql files will only be for gqlc and no other tool. Thus, gqlc should just be able to read them directly into memory from a remote location.

Describe the solution you'd like
If a filepath is remote, then fetch it and wrap it in a io.Reader

Describe alternatives you've considered
Downloading myself then processing *.gql files locally

feat(docs): add plugin route

This route will be for documenting both gqlc and community maintained plugins. It will also provide a walkthrough for implementing a plugin.

Golang Generator

Is your feature request related to a problem? Please describe.
Can't generate Go code

Describe the solution you'd like
Generation of Go code

JS Generator

Is your feature request related to a problem? Please describe.
Can't generate Javascript code

Describe the solution you'd like
Generation of Javascript code

bug(fetch): handle unauthorized/bad requests better

Describe the bug
Currently, if a request is made to a running service that requires authentication (a.k.a. Authorization header) the body is still tried to be read and an unexpected io.EOF is returned by something.

To Reproduce

gqlc --doc_out . https://api.github.com/graphql # Githubs' API requires a Personal Access Token

Expected behavior
The "unathorized/bad request" response should be converted into an error which can be passed up the call stack making it much easier for the user to see exactly what went wrong. A suggestion message could also be logged saying something like: It seems you forgot to provide an Authorization header. This can be done by, gqlc -H "Authorization=Bearer your_token".

type extension does not generate correct definition source

Given the schema

type Query {
    foo : Boolean!
}

extend type Query {
    bar : Boolean!
}

the compiler + golang generator produce

package main

import "github.com/graphql-go/graphql"

var QueryType = graphql.NewObject(graphql.ObjectConfig{
	Name: "Query",
	Fields: graphql.Fields{
		"foo": &graphql.Field{
			Type: graphql.NewNonNull(graphql.Boolean),
			Resolve: func(p graphql.ResolveParams) (interface{}, error) { return nil, nil }, // TODO
		},
	},
})

in which the Field "bar" is missing but expected.

feat(fetch): add retrying logic with exponential backoff

After some playing around with Github GraphQL API endpoint, the current client timeout isn't sufficient enough for most requests. Thus two routes can be chosen:

  1. Increase the timeout
  2. Add retrying logic with backoff

In my opinion, the retry with backoff is the better solution than just arbitrarily increasing the client timeout.

Type checking

Is your feature request related to a problem? Please describe.
Implement GraphQL document type checking

Describe the solution you'd like
Identify unimplemented types.

generate models from types and inputs

Currently, I am using part of the files generated by github.com/99designs/gqlgen w.r.t the models.

I propose to have a generator option that will also generate the structs for all the types and inputs defined in the schema.

chore(docs): fix readme

The README still references a few pre-refactoring things which no longer exist or make sense.

fix(fetch): ignore built in types and introspection types

All builtin GraphQL runtime types should be ignored since they are defined in the spec. These include Int, Float, String, @skip, @deprecated, etc.

Any internal types should also be ignored a.k.a the introspection types: __Type, __Field, etc.

Generator option flag

Is your feature request related to a problem? Please describe.
Like protoc, gqlc should support passing options to generators.

Describe the solution you'd like
Add option flag arg to compiler.RegisterGenerator() signature

Describe alternatives you've considered
Auto create option flag based on generator flag name (possibly trim "--" and "_out" from flag name to get generator name)

compiler depends on order of definition in file

consider the following example

type Permissions {
    shoppingCart: Boolean!
}

extend type Query {
    permissions: Permissions!
}

type Query

The compiler sees an extension on Query and fails immediately because it is defined later in the file. Such a file is created because we concatenate multiple files into one before using gqlc.

I think the compiler should use a two-phase method in resolving types and definitions ; first read all definitions then verify type completeness.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.