Code Monkey home page Code Monkey logo

graphql-union-input-type's Introduction

GraphQL Union Input Type

Union Input Type for GraphQL-js

Why

I wanted to represent a group of an arbitrary number of related, but different items and be able to work with it through graphql. When I started to write mutations, I realised, that Interfaces and Unions are not allowed. I'm not the only one - discussion goes here and here for example. There is a chance, that something like this will be added to the core, but it is not certain.

It would be nice to have some syntax support to specify the type to be validated against. Otherwise the only way to have clean queries without some workarounds is to let developers manually traverse AST, which seems like a too low level detail to expose.

Installation

npm install graphql-union-input-type

Usage

var UnionInputType = require('graphql-union-input-type');

UnionInputType(options);

Parameters

Options(object)

  • name(string) Name for the UnionType itself. It has to be unique in your schema and it can be used to mutate a union nested inside of another union.

  • inputTypes(array|object):

    • either array of GraphQLInputObjectType objects. Their name property will be referenced in mutations.
    • or object with name:GraphQLInputObjectType pairs. This name will be used instead.

    Objects returned by UnionInputType may also be used. This argument will be ignored if resolveType function is provided.

  • typeKey(string): a key in a mutation argument object containing the name of a type to validate against. If omitted, another strategy will be used instead.

  • resolveType(function(name) -> GraphQLInputObjectType|null): takes a name found in mutation argument and returns corresponding GraphQLInputObjectType or an object returned by UnionInputType. This strategy is not restricted by a predefined set of input types. It behaves as an interface in that UnionInputType does not know what input types implement it. If omitted, inputTypes is used.

  • resolveTypeFromAst(function(ast) -> GraphQLInputObjectType|null): provide this, if you absolutely do not want to explicitly specify the type in you mutation. The function will be called with full AST, which you can traverse manually to identify the input type and return it.

  • resolveTypeFromValue(function(value) -> GraphQLInputObjectType|null): same as resolveTypeFromAst, but for the case, where you use variables for your input types. The function is called with a variable value, and you need to return the input type

Examples

Create normal input types

var JediInputType = new GraphQLInputObjectType({
	name: 'jedi',
	fields: function() {
		return {
			side: {
				type: GraphQLString
			},
			name: {
				type: GraphQLString
			},
		}
	}
});

var SithInputType = new GraphQLInputObjectType({
	name: 'sith',
	fields: function() {
		return {
			side: {
				type: GraphQLString
			},
			name: {
				type: GraphQLString
			},
			doubleBlade: {
				type: GraphQLBoolean
			}
		};
	}
});

Combine them together

var HeroInputType = UnionInputType({
	name: 'hero',
	inputTypes: [JediInputType, SithInputType], //an object can be used instead to query by names other than defined in these types
	typeKey: 'side' //optional
});

OR

var HeroInputType = UnionInputType({
	name: 'hero',
	resolveType: function resolveType(name) {
		if (name === 'jedi') {
			return JediInputType;
		} else {
			return SithInputType;
		}
	},
	typeKey: 'side' //optional
});

OR

var HeroInputType = UnionInputType({
	name: 'hero',
	resolveTypeFromAst: resolveTypeFromAst(ast) {
		if (ast.fields[2] && ast.fields[2].name.value === 'doubleBlade') {
			return SithInputType;
		} else {
			return JediInputType;
		}
	}
});

Note, that in the last case as it is written doubleBlade field on SithInputType needs to be wrapped with GraphQLNonNull for the result to be accurate. Also you could loop through ast.fields instead of checking just position 2. For more information just dump AST into console and see what it contains.

Create schema

var MutationType = new GraphQLObjectType({
	name: 'mutation',
	fields: function() {
		return {
			hero: {
				type: GraphQLBoolean, //this is output type, normally it will correspond to some HeroType of type GraphQLUnionType or GraphQLInterfaceType
				args: {
					input: {
						type: HeroInputType //here is our Union
					}
				},
				resolve: function(root, args) {
					return true;
				}
			}
		};
	}
});

var schema = new GraphQLSchema({
	query: someQueryType,
	mutation: MutationType
});

Now you can call mutations on it

var query = `mutation {
	hero(input: {{kind: "sith", name: "Maul", saberColor: "red", doubleBlade: true})
}`;

graphql(schema, query).then(function(res) {
	expect(res.data).toBeDefined();
	done();
});

query = `mutation {
	hero(input: {{kind: "jedi", name: "Maul", saberColor: "red", doubleBlade: true})
}`;

graphql(schema, query).then(function(res) {
	expect(res.errors).toBeDefined();
	done();
});

The second query will fail to validate, as there is no doubleBlade field on jedi type. Of course you can also set the type of your mutation field to something other than GraphQLBoolean and specify the desired return schema.

You can also omit typeKey property and write the same mutation this way:

var query = `mutation {
	hero(input: {_type_: "sith", _value_: {name: "Maul", saberColor: "red", doubleBlade: true}})
}`;

Your resolve function for mutation arguments will get this input argument as is.

Finally if you provided resolveTypeFromAst function, you may query with an input argument as it is:

var query = `mutation {
	hero(input: {name: "Maul", saberColor: "red", doubleBlade: true})
}`;

Using variables (since 0.3.0)

If you want to use variables, you may do it as you normally would. Please note, that the name of an input variable is a name property passed to the UnionInputType function. In terms of this readme it will be hero:

var query = `mutation($hero: hero!) {
	hero(input: $hero)
}`;

There is a function resolveTypeFromValue, which is similar to resolveTypeFromAst, but used for a variable value.

Capabilities

You can use these unions as mutation arguments, nest them inside any input types and even create unions of unions. The only small problem is that objects returned by UnionInputType are really GraphQLScalarType, so I had to allow scalars to be passed to the function.

You may use variables since 0.3.0. This issue might have some relevant information.

Tests

Test are written for jasmine. I use nodemon to run them. You can find more examples in the spec file. The last test is not written formally, I just used it to play around with nested structures.

Since 0.3.0 just use npm run test

graphql-js 0.10.1 breaks tests. Seems to be a bug. Reported here

Contributing

Feel free to make suggestions or pull requests.

License

(The MIT License)

Copyright (c) 2016 Sergei Petrov

graphql-union-input-type's People

Contributors

aneilbaboo avatar blesswinsamuel avatar cardinal90 avatar fubhy avatar shimeez avatar wrak 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  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

graphql-union-input-type's Issues

Type validation when using variables

Hello,
When using variables in query, the inputType never get validated.
Assuming we're using your Star Wars schema, the following will work, whatever the side input in variables :

var HeroInputType = UnionInputType({
    name: 'hero',
    inputTypes: [JediInputType, SithInputType], //an object can be used instead to query by names other than defined in these types
    typeKey: 'side'
});

[...]

graphql(Schema, `
  mutation($hero: HeroInputType!) {
    hero(input: $hero) {
      ...
    }
  }
`, {}, {}, {
  hero: {
    side: 'WHATEVER'
  }
});

Is there a way to integrate this with Apollo Server

I just started with graphql (and apollo for that matter), apollo makes use of custom resolvers and doesn't explicitly deal with creating new Graph* types normally as far as I can tell. This is certainly something I would like to be able to do though, specifically passing a list of unions.

Update graphql dependency

Thank you for making an awesome project!

It seems like graphql-union-input-type still has graphql 0.10 as the dependency.

Any plan on updating the graphql dependency version?

Any chance of a wrapper for native gql and makeExecutableSchema?

First off, I have to say this is amazing and should be part of core graphql. There's so many limitations introduced without this feature, so thank you.

Is there any chance this could somehow be implemented with graphql-tools makeExecutableSchema? I find that a large number of larger projects tend to implement their gql schemas with this tool, and if a wrapper function could be made that translates the native gql templated language into the required format a lot more people might find and use this tool.

graphql-tools: https://www.npmjs.com/package/graphql-tools
An example of a wrapper-function that enables inheritance, generic typing, etc in schemas:
https://www.npmjs.com/package/graphql-s2s
(Ideally there would be a similar wrapper function such as: transpileUnionInputType(schema))

Types not resolved when passing in part of an object as a variable

I've tried many combinations of implementing resolveTypeFromAst, resolveType, resolveTypeFromValue, but none of these seem to work when passing in just the name argument, for instance.

var query = `mutation($name: String) {
  hero(input: {
    name: $name,
    side: "jedi"
  })
}`;

I get an error along the lines of:

Expected type "hero", found {name: $name, side: "jedi"}.

This works fine if you query directly with no variables:

`mutation {
  hero(input: {
    name: "foo",
    side: "jedi"
  })
}`;

Any help would be appreciated!

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.