@nyteshade/ne-schemata v3.6.1
ne-schemata


GraphQL IDL/Schemata String class to make working with GraphQL easier
NOTE!!
Henceforth, this library will be called @nyteshade/ne-schemata. Please this npm library will not be maintained on this name after this release.
Overview
Working with Schemata (Schema Definition Language), sometimes called IDL (Interface Definition Language) can occasionally be difficult. A schema definition is the most concise way to specify a schema in GraphQL.
Classes in programming languages are data structures that associated properties and functions designed to work with them. The provided Schemata class extends String and should be able to be used in most places in JavaScript where strings are used with no changes. This, however, is where things change.
Some of the most often applied tasks with SDL are building a schema object, building an executable schema object, parsing it into ASTNode objects. Occasionally a given bit of SDL is checked for validity.
The Schemata class does all this and more.
Features
Biggest selling points of working with the Schemata class
- Creation of bound or non-bound
GraphQLSchemaandASTNodeobject - Storage of an associated
resolversmap - Storage of an associated
schemaobject for iteration and modification - Checking of the validity of SDL as a
ASTNodeas well as aGraphQLSchema - Merge SDL together via AST parsing
- Merge multple GraphQLSchemas via AST parsing
- Pare down one SDL/Schema using another as a guide
Recent Breaking Changes
2.4.0, 2.4.1 -> 3.0.1
2.4.0 and 2.4.1 are both actually breaking changes. Tests have been updated and version updated appropriately for 3.0.0. The breaking changes are as follows:
// Previously
let schemata = gql`type Query { name: String }` // -> Schemata instance
// Currently
let ast = gql`type Query { name: String }` // -> Proxy(ASTNode) with access to SchemataThe gql template string function no returns an AST node as is inline with with
Apollo GraphQL's implementation of a template string with the same name. This allows
compatibility to be increased.
The returned AST object is wrapped in a Proxy instance allowing access to schemata
instance properties, a string and schemata property accessor that provide access
to a String representation of the SDL and the instance of Schemata that represents
the templated string
The Proxy instance allows direct access to any Schemata instance property that does
not directly conflict with a AST node property of the same name. If the AST node
property conflicts, access will be limited to ast.schemata.property where ast is
the result of calling gql and property is the desired conflicting property.
New Changes
You can now require files with .graphql, .gql, and .sdl extensions. If found, the
contents of the file will be used as parameters for a call to new Schemata(). Various
obvious aspects of the file can be imported specifically as follows
astNode - an ASTNode document object representing the SDL contents
of the .graphql file contents. Null if the text is invalid
resovlers - if there is an adjacent file with the same name, ending in
.js and it exports either a `resolvers` key or an object by
default, this value will be set on the sdl object as its set
resolvers/rootObj
schema - a GraphQLSchema instance object if the contents of the .graphql
file represent both valid SDL and contain at least one root
type such as Query, Mutation or Subscription
sdl - the string of SDL wrapped in an instance of Schemata
typeDefs - the raw string used that `sdl` wraps
default - the sdl string wrapped in an instance of Schemata is the
default exportNotably, in 3.x and onward, if one of these extensions is require'd, and
adjacent to the file is a .js file with the same name, its contents (or .resolvers
if present) will be used as the resolvers for the generated Schemata instance.
Installation
Simply npm or yarn install the package
/Volumes/harddrive/path/to/your/project
$ npm install --save ne-schemataUsage
Using new
Creating instances of Schemata can of course be done with typical JavaScript via the new keyword.
let schemata = new Schemata(
'type Person { name: String } type Query { peep: Person }'
)Using Schemata.from
Creating instances of Schemata can also be done with a constant on the class itself called .from().
let schemata = Schemata.from(
'type Person { name: String } type Query { peep: Person }'
)From an existing instance of Schemata
Creating instances of Schemata from other instances of Schemata is also supported. In these cases, all the set values of note will be moved over. This includes schema and resolvers set on the original instance.
let sdlA = Schemata.from(
'type Person { name: String } type Query { peep: Person }',
{
Query: {
peep() {
return { name: 'Brielle' }
},
},
}
)
let sdlB = Schemata.from(sdlA)
console.log(sdlB.resolvers) // { Query: { peep: [function peep()] } }From an instance of GraphQLSchema
You can also create a Schemata instance from an existing instance of GraphQLSchema. The typeDefs or SDL string will be generated using printSchema() from the base graphql module. The .schema value will be set explicitly to the value supplied in the case it is executable or has had other modifications performed on it.
import { makeRemoteExecutableSchema, introspectSchema } from 'graphql-tools';
import { HttpLink } from 'apollo-link-http';
import fetch from 'node-fetch';
let link = new HttpLink({ uri: 'some uri', fetch })
let schema = makeRemoteExecutableSchema(await introspectSchema(link), link)
let schemata = new Schemata(schema) // or Schemata.from(schema)
console.log(schemata) // the SDL from the remote schemaUsing the gql template function
Alternatively, creating a Schemata string instance can be done using the gql template function. The function was named as many popular editors have a syntax highlighting theme that highlights GraphQL SDL. It seemed to make a lot of sense to add the resulting string from this as an instance of Schemata
import { gql } from 'ne-schemata'
let schemata = gql`
type Person {
name: String
}
type Query {
peep: Person
}
`
let schema = schemata.schemaSDL or typeDefs with extend type
SDL or typeDefs with extend type in them will honored and types will be extended as directed by the SDL. Calling .sdl or .typeDefs will contain the extend type SDL. .schema will return a GraphQLSchema object with the types properly extended however. To modify and update the internal SDL, call .flattenSDL(). This will regenerate the SDL on the instance. If, instead, you simply wish to see what the flattened SDL string looks like without modifying the instance, this can be found by accessing the .flatSDL property.
// Create instance with extend type in SDL
let schemata = gql`type User { name: String } extend type User { age: Int }`
console.log(schemata)
// Prints
// type User { name: String } extend type User { age: Int }
console.log(schemata.flatSDL)
// Prints
// type User {
// name: String
// age: Int
// }
schemata.flattenSDL()
console.log(schemata)
// Prints
// type User {
// name: String
// age: Int
// }Using require('file.graphql') with require extensions
The final way to create new instances of Schemata in a helpful and unobtrusive manner is to use the '.graphql' extension handler. This handler registers the '.graphql' extension with the require() function in your local nodeJS environment.
Once registered, require() and import {} from will now do several interesting things with '.graphql' imported text. First the resulting exported object has 5 different keys, with the default being a Schemata string wrapping the contents of the file itself.
The five exports of imported '.graphql' files are:
- astNode an
ASTNodeobject representing the parsed contents of the file - resolvers if there is an adjacent .js file of the same name, and if that file exports
'resolvers'or if the exported results are an object and not a function, then that object is considered to be a resolvers map for the associated.graphqlfile. - schema generated from the contents of the Schemata string. If the resolvers object was successfully generated and imported, this will be an executableSchema instead.
- sdl the Schemata string instance, also the default export
- typeDefs keeping in line and adding compatibility with Apollo Server, the
typeDefsexport is simply a synonym forsdl.
How do you use the extension? Somewhere, typically early on in the project entry script, make a call to register() and then subsequently import/require your '.graphql' files.
require('ne-schemata/register')
import { sdl, schema, resolvers } from './graphql/Person.graphql'
// or alternatively
require('ne-schemata').register('graphql') // or simply register() as .graphql is the default
import { sdl, schema, resolvers } from './graphql/Person.graphql'Schemata Merging (both strings and GraphQLSchema objects)
AST merging of multiple SDL files and resolver maps. This will allow you to do something like the following. Merging schemas is just as easy. A new instance of GraphQLSchema will be created and stored on the instance but the pre-existing resolvers will be deeply merged with the supplied resolvers on the schema to be merged and all will be applied and bound to the newly generated schema.
import graphqlHTTP from 'express-graphql'
import Express from 'express'
import { gql } from 'ne-schemata'
const app = Express()
const sdlA = gql`
type Person {
name: String
}
type Query {
peep: Person
}
`
const sdlB = gql`
type Person {
age: Int
}
type Query {
peeps: [Person]
}
`
const sdlC = sdlA.mergeSDL(sdlB)
/*
sdlC.schema would evaluate to something like the following:
type Person {
name: String
age: Int
}
type Query {
peep: Person
peeps: [Person]
}
*/
app.use(
'/graphql',
graphqlHTTP({
schema: sdlC.schema,
graphiql: true,
})
)
app.listen(4000)Merging Concerns
TLDR; Schemata has your back and you don't really need to read this
Still here? Cool, let's learn what happens during merging. When merging multiple schemas, one of the problems that can occur, especially if one of the schemas being merged is a remote executable schema, is that the resolvers might be bound to an older version of a schema that is only a subset of the new merge.
Schemata helps to alleviate this problem by introducing a few interfaces of note. The immediate solution to this problem is to wrap the resolvers and have those resolvers point to a newly merged GraphQLSchema object rather than the one it was originally bound to.
The problem comes in when you try to merge more than once. Wrapping a previously wrapped version of a resolver will allow you to set it at the top, but the previous attempt to do will execute and override the changes you just made.
Schemata solves this problem by storing the older unbound methods with each bind. A reference to the schema, sdl and resolvers at the time of the merge are kept. When each subsequent merge occurs, these older unwrapped/unbound resolvers are used to apply the new schema rather than layering things like Russian nesting dolls.
Furthermore, when making a call to .mergeSchema() you may add your own resolver injectors which are simply objects containing a property called '.resolverInjectors' which are an array of ResolverArgsTransformer functions. These give you full acceess to the source, args, context and info parameters to modify to your hearts content before the original unwrapped resolver receives those parameters.
import { Schemata } from 'ne-schemata'
let a = Schemata.from('type Query { me: String }', {
me(s, a, c, i) {
return 'Brielle'
},
})
let b = Schemata.from('type Query { her: String }', {
her(s, a, c, i) {
return 'Stacy'
},
})
let ab = a.mergeSchema(b)
console.log(ab.prevMergeResolvers)
// [{schema:..., sdl:..., resolvers:...}, ...] <-- unwrapped
console.log(ab.resolvers)
// { me(), her() } <-- wrapped to new schema
let c = Schemata.from('type Polygon { sides: Int }', {
Polygon: {
sides(s, a, c, i) {
return 6
},
},
})
let abc = ab.mergeSchema(c)
console.log(abc.prevMergeResolvers)
// [{schema:..., sdl:..., resolvers:...}, ...] <-- unwrapped
console.log(abc.resolvers)
// { me(), her(), Polygon: { sides() } } <-- wrapped to new schemaEven this example has difficulty explaining properly what is happening and why it's important, but as each merge happens the previous unwrapped resolvers are passed along and newly introduced schemas' resolvers are added to this list.
Schemata Class Properties and Methods
- Constructor
- Instance properties
- .ast:
?ASTNode - .executableSchema:
?GraphQLSchema - .flatSDL:
string - .graphiql:
boolean - .hasAnExecutableSchema:
boolean - .hasFlattenedResolvers:
boolean - .prevResolverMaps:
Array<ExtendedResolverMap> - .resolvers:
?ResolverMap - .rootValue:
?ResolverMap - .schema:
?GraphQLSchema - .schemaDirectives:
Object - .schemaDirectives:
Object - .sdl:
string - .typeDefs:
string - .types:
Object - .valid:
boolean - .validSchema:
boolean - .validSDL:
boolean
- .ast:
- Instance methods
- astFieldByName
- astTypeByName
- buildResolverForEachField
- buildResolvers
- clearResolvers
- clearSchema
- flattenSDL
- forEachEnum
- forEachField
- forEachInputObjectField
- forEachInputObjectType
- forEachInterface
- forEachInterfaceField
- forEachOf
- forEachRootType
- forEachScalar
- forEachType
- forEachTypeField
- forEachUnion
- merge
- mergeSchema
- mergeSDL
- pareSDL
- run
- runAsync
- schemaFieldByName
- schemaResolverFor
- Static properties
- .ALL:
Number - .ENUMS:
Number - .gql:
Module - .HIDDEN:
Number - .INPUT_TYPES:
Number - .INTERFACES:
Number - .ROOT_TYPES:
Number - .SCALARS:
Number - .TYPES:
Number - .UNIONS:
Number
- .ALL:
- Symbols
- Static methods
- Exported types
- External Functions
- Default Functions
- Additional Goodies
Constructor ✯
constructor(
typeDefs: SchemaSource,
resolvers: ResolverMap = null,
buildResolvers: boolean | string = false,
flattenResolvers: boolean = false
): SchemataSchemata instances are versatile and can be created through a variety of sources. Typically anything that can be converted to a string of SDL can be used to create a new Schemata instance. Everything from a GraphQL Source instance, previously instantiated Schemata instance, a GraphQLSchema object or even an ASTNode. Of course, basic strings of SDL can also be used.
An object with resolver functions can be supplied as the second parameter allowing executable schemas to be generated from the source SDL.
The final two parameters are for the case where the Schemata instance is initialized with a GraphQLSchema instance or with an older instance of Schemata.
- The first causes a programmatic attempt to generate and set an object containing resolvers from the executable schema represented by either eligible object. If the string 'all' is supplied than a resolver for each field not defined will receive GraphQL's
defaultFieldResolveras its resolver. - The second causes the generated resolver map to be in the flattened style with root fields exposed at the root of the resolver map. By default the Apollo style resolver map with fields under their respective type names is the default
Instance properties ✯
.ast ✯
generates ASTNodes that the internal string would generate when passed to require('graphql').parse()
.schema ✯
retrieves any internally stored GraphQLSchema instance generated thus far; if one does not exist, one will be generated
sets the value of the internal GraphQLSchema storage variable
.executableSchema ✯
retrieves the internally stored executable schema, if one exists, or it creates one if the .resolvers value has been set and if there is valid .sdl. This getter is now deprecated. All functionality has been integrated in the single .schema property without any loss of functionality. Please use that instead.
.flatSDL ✯
working from the built in .schema GraphQLSchema instance, SDL is regenerated and printed. Any extend type declarations applied to the schema instance will be merged and only the single type will be shown. This will return a string of SDL but the instance will not be modified in the process.
.graphiql ✯
retrieves the .graphiql flag which defaults to true. This might be used by express-graphql. Schemata does not use this flag internally, but rather stores it as a convenience property.
sets the internal graphiql flag to a value other than true. Remember that this field is not used internally by Schemata other than storage and retrieval.
.hasAnExecutableSchema ✯
A getter to determine if an executable schema is attached to this Schemata instance. It does so by walking the schema fields via buildResolvers() and reporting whether there is anything inside the results or not.
.hasFlattenedResolvers ✯
Determines if the internal resolver map has at least one defined root type resolver (Query, Mutation, Subscription) or not. If not, including if there are no root resolvers (yet), this property will evaluate to false
.prevResolverMaps ✯
When a Schemata instance is merged with another GraphQLSchema, its resolvers get stored before they are wrapped in a function that updates the schema object it receives. This allows them to be wrapped safely at a later date should this instance be merged with another.
retrieves any internally stored resolver maps
sets the internally stored resolver maps
.schemaDirectives ✯
Schemata is designed to be used with various GraphQL JavaScript engines. In
many cases, it is simply used as a variable to store configuration for more
specific calls to things like makeExecutableSchema from graphql-tools or
GraphQLServer and the like. For more information on Apollo's usage of
this property, see this page:
https://www.apollographql.com/docs/graphql-tools/schema-directives.html
An object containing the name to function mapping for schema directive classes when used with apollo-server or graphql-yoga.
Internally sets the instance variable for the schemaDirectives object used with apollo-server or graphql-yoga
.sdl ✯
the same value as the string this instance represents
.types ✯
calculates a JavaScript object based on the schema where type names are sub-objects, and their keys are field names with values containing objects with two keys. Those keys are type and args. The type field will evaluate to the SDL type of the field in question and the args field will evaluate to an order specific set of objects with an argument name key and SDL argument type value.
For clarity, given a schema such as the following:
type A {
a: String
b: [String]
c: [String]!
}
type Query {
As(name: String): [A]
}One can expect a return type like this:
{
Query: {
As: { type: '[A]', args: [ { name: 'String' } ] }
},
A: {
a: { type: 'String', args: [] },
b: { type: '[String]', args: [] },
c: { type: '[String]!', args: [] }
}
}.typeDefs ✯
a synonym for .schemata, ideal for use with Apollo and other librariees that expect this nomenclature
.rootValue ✯
a synonym for .resolvers, ideal for use with express-graphql or other libraries that expect this nomenclature. Note: that the format of this object is slightly different for Apollo than the Facebook reference implementation
.resolvers ✯
retrieves any internally store resolvers map
sets the internally stored resolvers map
.validSDL ✯
if the SDL this string represents can be parsed without error, true will be returned; false otherwise
.validSchema ✯
if the SDL this string represents can be used to build a schema without error, true will be returned; false otherwise
.valid ✯
returns true if both .validSDL and .validSchema are both true
Instance methods ✯
astFieldByName ✯
astFieldByName(type: string, field: string): FieldNodeFor SDL that doesn't properly build into a GraphQLSchema, it can still be searched for a type and field.
astTypeByName ✯
astTypeByName(type: string): ASTNodeFor SDL that doesn't properly build into a GraphQLSchema, it can still be parsed and searched for a type by name.
buildResolvers ✯
buildResolvers(
flattenRootResolversOrFirstParam: boolean | ResolverMap,
...extendWith: Array<ResolverMap>
): ResolverMapIn the case where Schemata instance was created with a GraphQLSchema, especially one that is an executableSchema or somehow has resolvers in it already, buildResolvers() will walk the schema fields and build up a resolvers object of those resolve() functions on each field. If true is supplied as the first parameter, the resolver functions from Query, Mutation and Subscription will appear in the root of the returned object rather than under their aforementioned parent types.
buildResolverForEachField ✯
buildResolverForEachField(
flattenRootResolversOrFirstParam: boolean | ResolverMap,
...extendWith: Array<ResolverMap>
): ResolverMapFrom time to time it makes more sense to wrap every possible resolver mapping in given schema. Getting a handle to each fields resolver and or substituting missing ones with GraphQL's defaultFieldResolver can be a tiresome affair. This method walks the schema for you and returns any previously defined resolvers alongside defaultFieldResolvers for each possible field of every type in the schema.
If a schema cannot be generated from the SDL represented by the instance of Schemata, then an error is thrown.
clearResolvers ✯
clearResolvers()Removes any existing .resolvers map that might have been assigned to the object instance. This is identical to calling .resolvers = null, and only exists here as a semantic method that may have future functionality added to it.
clearResolvers ✯
clearSchema()Removes any existing .schema GraphQLSchema instance that might have been assigned to the object instance. This is identical to calling .schema = null, and only exists here as a semantic method that may have future functionality added to it.
flattenSDL ✯
flattenSDL()Working from the built in .schema GraphQLSchema instance, SDL is regenerated and printed. Any extend type declarations applied to the schema instance will be merged and only the single type will be shown. The internal typeDefs will be modified after this call and subsequent calls to .ast, .schema, .sdl, or .typeDefs will no longer have the extend type directives listed.
forEachOf ✯
forEachOf(
fn: ForEachOfResolver,
context: mixed,
types: number = Schemata.TYPES,
suppliedSchema: ?GraphQLSchema = null
): GraphQLSchemaIterates over the type map of either the internal schema or one created from the SDL string this instance represents and then stored for later use. See above for the definition of the ForEachOfResolver callback signature. types is a bitmask made up of at least one of the constant properties on Schemata such as Schemata.ALL or Schemata.TYPES and optionally augmented with Schemata.HIDDEN
See above for the definition of the ForEachOfResolver callback signature
forEachType ✯
forEachType(
fn: ForEachOfResolver,
context: mixed,
suppliedSchema: ?GraphQLSchema
): GraphQLSchemaIterates over all types on either the internal schema or one created from the SDL string this instance represents and then stored for later use
See above for the definition of the ForEachOfResolver callback signature
forEachInputObjectType ✯
forEachInputObjectType(
fn: ForEachOfResolver,
context: mixed,
suppliedSchema: ?GraphQLSchema
): GraphQLSchemaIterates over all input types on either the internal schema or one created from the SDL string this instance represents and then stored for later use
See above for the definition of the ForEachOfResolver callback signature
forEachUnion ✯
forEachUnion(
(fn: ForEachOfResolver),
(context: mixed),
(suppliedSchema: ?GraphQLSchema)
)Iterates over all the unions on either the internal schema or one created from the SDL string this instance represents and then stored for later use
See above for the definition of the ForEachOfResolver callback signature
forEachEnum ✯
forEachEnum(
(fn: ForEachOfResolver),
(context: mixed),
(suppliedSchema: ?GraphQLSchema)
)Iterates over all enums on either the internal schema or one created from the SDL string this instance represents and then stored for later use
See above for the definition of the ForEachOfResolver callback signature
forEachInterface ✯
forEachInterface(
(fn: ForEachOfResolver),
(context: mixed),
(suppliedSchema: ?GraphQLSchema)
)Iterates over all interfaces on either the internal schema or one created from the SDL string this instance represents and then stored for later use
See above for the definition of the ForEachOfResolver callback signature
forEachScalar ✯
forEachScalar(
(fn: ForEachOfResolver),
(context: mixed),
(suppliedSchema: ?GraphQLSchema)
)Iterates over all scalars on either the internal schema or one created from the SDL string this instance represents and then stored for later use
See above for the definition of the ForEachOfResolver callback signature
forEachRootType ✯
forEachRootType(
(fn: ForEachOfResolver),
(context: mixed),
(suppliedSchema: ?GraphQLSchema)
)Iterates over the three root types (Query, Mutation and Subscription) on either the internal schema or one created from the SDL string this instance represents and then stored for later use
See above for the definition of the ForEachOfResolver callback signature
forEachField ✯
forEachField(
(fn: ForEachFieldResolver),
(context: mixed),
(types: number = ALL),
(suppliedSchema: ?GraphQLSchema = null)
)Iterates over all fields on all types on either the internal schema or one created from the Schemata string this instance represents and then stored for later use. See above for the definition of the ForEachFieldResolver callback signature. types is a bitmask made up of at least one of the constant properties on Schemata such as Schemata.ALL or Schemata.TYPES and optionally augmented with Schemata.HIDDEN
See above for the definition of the ForEachFieldResolver callback signature
forEachTypeField ✯
forEachTypeField(
(fn: ForEachFieldResolver),
(context: mixed),
(suppliedSchema: ?GraphQLSchema = null)
)Iterates over all type fields on all types on either the internal schema or one created from the SDL string this instance represents and then stored for later use.
See above for the definition of the ForEachFieldResolver callback signature
forEachInterfaceField ✯
forEachInterfaceField(
(fn: ForEachFieldResolver),
(context: mixed),
(suppliedSchema: ?GraphQLSchema = null)
)Iterates over all interface fields on all types on either the internal schema or one created from the SDL string this instance represents and then stored for later use
See above for the definition of the ForEachFieldResolver callback signature
forEachInputObjectField ✯
forEachInputObjectField(
(fn: ForEachFieldResolver),
(context: mixed),
(suppliedSchema: ?GraphQLSchema = null)
)Iterates over all input object fields on all types on either the internal schema or one created from the SDL string this instance represents and then stored for later use.
See above for the definition of the ForEachFieldResolver callback signature
merge ✯
merge(
schema: SchemaSource,
config?: MergeOptionsConfig = DefaultMergeOptions
): SchemataA new Schemata object instance with merged schema definitions as its contents as well as merged resolvers and newly bound executable schema are all created in this step and passed back. The object instance itself is not modified
Post merge, the previously stored and merged resolvers map are are applied and a new executable schema is built from the ashes of the old.
mergeSDL ✯
mergeSDL(
schemaLanguage: string | Schemata | Source | GraphQLSchema,
conflictResolvers: ?ConflictResolvers = DefaultConflictResolvers
): SchemataGiven a string of Schema Definition Language (or SDL) or a Schemata instance itself or any of the other initialization sources, the function will proceed to blend the derived SDL with the one of the instance. In the case of conflicts, either the default resolver, which simply takes the newer right hand value, or a function of your own devising will be called to decide. See the ConflictResolvers type for more info
pareSDL ✯
pareSDL(
schemaLanguage: string | Schemata | Source | GraphQLSchema,
resolverMap: ?ResolverMap = null
): SchemataGiven a string of Schema Definition Language (or SDL) or a Schemata instance itself or any of the other initialization sources, the function will proceed to remove the items using the derived SDL as a guide. If the internal instance has resolvers set or one can be built from the Schema stored on the instance, or if a set is supplied as the second parameter, the resolvers will also be pared down for any removed types and fields. Types stripped of all fields will themselves be removed.
mergeSchema ✯
mergeSchema(
schema: GraphQLSchema | Schemata,
config?: MergeOptionsConfig = DefaultMergeOptions
): SchemataShortcut for the merge() function; mergeSDL still exists as an entity of itself, but merge() will invoke that function as needed to do its job and if there aren't any resolvers to consider, the functions act identically.
run ✯
run (
query: string | Source,
contextValue?: mixed,
variableValues?: ?ObjMap<mixed>,
rootValue?: mixed,
operationName?: ?string,
fieldResolver?: ?GraphQLFieldResolver<any,any>
)A convenient pass-thru to graphqlSync() to query the schema in a synchronous manner. The schema used is built from the contents of the Schemata string itself. If there is a .resolvers map set on the instance, it will be passed for the root object/value parameter.
runAsync ✯
async runAsync(
query: string | Source,
contextValue?: mixed,
variableValues?: ?ObjMap<mixed>,
rootValue?: mixed,
operationName?: ?string,
fieldResolver?: ?GraphQLFieldResolver<any,any>
)A convenient pass-thru to graphql() to query the schema in an asynchronous manner. The schema used is built from the contents of the Schemata string itself. If there is a .resolvers map set on the instance, it will be passed for the root object/value parameter.
schemaFieldByName ✯
schemaFieldByName(type: string, field: string): FieldNodeBuilds a schema based on the SDL in the instance and then parses it to fetch a named field in a named type. If either the type or field are missing or if the SDL cannot be built as a schema, null is returned.
schemaResolverFor ✯
schemaResolverFor(type: string, field: string): ?FunctionA method to fetch a particular field resolver from the schema represented by this Schemata instance.
Static properties ✯
.gql ✯
a reference to the results of require('graphql')
.ALL ✯
used with forEachOf/forEachField() methods to denote that all GraphQLTypes should be iterated over. Can be combined with bit-wise OR'ing.
.TYPES ✯
used with forEachOf/forEachField() methods to denote that all types should be iterated over. Can be combined with bit-wise OR'ing.
.INTERFACES ✯
used with forEachOf/forEachField() methods to denote that all interfaces should be iterated over. Can be combined with bit-wise OR'ing.
.ENUMS ✯
used with forEachOf/forEachField() methods to denote that all enums should be iterated over. Can be combined with bit-wise OR'ing.
.UNIONS ✯
used with forEachOf/forEachField() methods to denote that all unions should be iterated over. Can be combined with bit-wise OR'ing.
.SCALARS ✯
used with forEachOf/forEachField() methods to denote that all scalars should be iterated over. Can be combined with bit-wise OR'ing.
.ROOT_TYPES ✯
used with forEachOf/forEachField() methods to denote that all root types (Query, Mutation, Subscription) should be iterated over. Can be combined with bit-wise OR'ing.
.INPUT_TYPES ✯
used with forEachOf/forEachField() methods to denote that all input object types should be iterated over. Can be combined with bit-wise OR'ing.
.HIDDEN ✯
used with forEachOf/forEachField(). When iterating the internal meta types that start with underscores are not iterated over. By combining this flag via bit-wise OR'ing, the internal meta types will be included
Symbols ✯
.iterator ✯
get [Symbol.iterator](): FunctionRedefine the iterator for Schemata instances so that they simply iterate over the contents of the SDL/typeDefs.
.species ✯
get [Symbol.species](): FunctionSymbol.species ensures that any String methods used on this instance will result in a Schemata instance rather than a String. NOTE: this does not work as expected in current versions of node. This bit of code here is basically a bit of future proofing for when Symbol.species starts working with String extended classes
.toStringTag ✯
get [Symbol.toStringTag](): FunctionEnsures that instances of Schemata report internally as Schemata object. Specifically using things like Object.prototype.toString.
Static methods ✯
buildFromDir ✯
static async buildFromDir(
path: string
): Promise<Schemata>Finally a way to recursively look through a directory for any .graphql, .gql, or any .sdl files
and use their contents for a merged typeDefs. It also will build up an object of resolvers to be set
on the final instance of Schemata.
Loading the schema for the site has now become as easy as
let siteSchemata = await Schemata.buildFromDir('/path/to/src/with/.graphql/files')buildSchema ✯
static buildSchema(
sdl: string | Source | Schemata | GraphQLSchema,
showError: boolean = false,
schemaOpts: BuildSchemaOptions & ParseOptions = undefined
): ?GraphQLSchemaUsing the Facebook reference implementation's call to buildSchema() is made. If an error is thrown and showError is not true, the exception will be swallowed and null will be returned instead. schemaOpts is the optional second parameter taken by require('graphql').buildSchema()
parse ✯
static parse(
sdl: string | Schemata | Source | GraphQLSchema,
showError: boolean = false
): ?ASTNodeUsing the Facebook reference implementation's call to parse() is made. The resulting AstNode objects will be returned. If the sdl is not valid, an error will be thrown if showError is truthy
print ✯
static print(
ast: ASTNode,
showError: boolean = false
): ?SchemataA little wrapper used to catch any errors thrown when printing an ASTNode
to string form using require('graphql').print(). If showError is true
any thrown errors will be rethrown, otherwise null is returned instead.
Should all go as planned, an instance of Schemata wrapped with the printed
SDL will be returned.
Since version 1.7
from ✯
static from(
typeDefs: string
| Source
| Schemata
| GraphQLSchema
| ASTNode,
resolvers: ?ResolverMap = null,
buildResolvers: boolean | string = false,
flattenResolvers: boolean = false,
): SchemataAn alterate way of creating a new instance of Schemata. Effectively equivalent to new Schemata(...)
Exported Types ✯
fromContentsOf ✯
static async fromContentsOf(
path: string
): Promise<Schemata>Sometimes you just want to grab the contents of the SDL from disk and pass it to Schemata.from()
ConflictResolvers ✯
export type ConflictResolvers = {
/** A handler for resolving fields in matching types */
fieldMergeResolver?: FieldMergeResolver,
/** A handler for resolving directives in matching types */
directiveMergeResolver?: DirectiveMergeResolver,
/** A handler for resolving conflicting enum values */
enumValueMergeResolver?: EnumMergeResolver,
/** A handler for resolving type values in unions */
typeValueMergeResolver?: UnionMergeResolver,
/** A handler for resolving scalar config conflicts in custom scalars */
scalarMergeResolver?: ScalarMergeResolver,
}The ConflictResolvers is simply an object that defines one or more of the handler functions mentioned above. FieldMergeResolvers are the most common types, however the others can be useful in schema and Schemata merging as well. If no resolvers are supplied, the default ones are used. These simply overrite the lefthand (instance) value with the righthand (supplied) value
AsyncEntryInspector ✯
type AsyncEntryInspector = (
key: string,
value: Function,
path: Array<string>,
map: ResolverMap
) => ?Promise<{ [string]: Function }>An AsyncEntryInspector is a function passed to asyncWalkResolverMap that is invoked for each encountered pair along the way as it traverses the ResolverMap in question. The default behavior is to simply return the supplied entry back.
If undefined is returned instead of an object with a string mapping to a Function, then that property will not be included in the final results of asyncWalkResolverMap.
EntryInspector ✯
type EntryInspector = (
key: string,
value: Function,
path: Array<string>,
map: ResolverMap
) => { [string]: Function }An EntryInspector is a function passed to walkResolverMap that is invoked for each encountered pair along the way as it traverses the ResolverMap in question. The default behavior is to simply return the supplied entry back.
If undefined is returned instead of an object with a string mapping to a Function, then that property will not be included in the final results of walkResolverMap.
DirectiveMergeResolver ✯
export type DirectiveMergeResolver = (
leftType: ASTNode,
leftDirective: DirectiveNode,
rightType: ASTNode,
rightDirective: DirectiveNode
) => DirectiveNodeThe DirectiveMergeResolver is a function that takes both left and right types as well as left and right directives. The function decides which directive to return and does so.
EnumMergeResolver ✯
export type EnumMergeResolver = (
leftType: ASTNode,
leftValue: EnumValueNode,
rightType: ASTNode,
rightValue: EnumValueNode
) => EnumValueNodeThe EnumMergeResolver is a function that takes both left and right types as well as left and right enum values. The function decides which enum value to return and does so.
FieldMergeResolver ✯
export type FieldMergeResolver = (
leftType: ASTNode,
leftField: FieldNode,
rightType: ASTNode,
rightField: FieldNode
) => FieldNodeThe FieldMergeResolver is a function that takes both left and right types as well as left and right field values. The function decides which field value to return and does so.
ForEachFieldResolver ✯
export type ForEachFieldResolver = (
type: mixed,
typeName: string,
typeDirectives: Array<GraphQLDirective>,
field: mixed,
fieldName: string,
fieldArgs: Array<GraphQLArgument>,
fieldDirectives: Array<GraphQLDirective>,
schema: GraphQLSchema,
context: mixed
) => voidThe ForEachFieldResolver function definition is about a function that takes a callback that receives the type object, the type object name, an array of directives if any, the field object, the field object name, any field arguments, any field directives, the schema and any supplied context to use in the iteration and looping over the types defined in the Schematas schema representation. If an internal store of a schema is not availabe, one is created
ForEachOfResolver ✯
export type ForEachOfResolver = (
type: mixed,
typeName: string,
typeDirectives: Array<GraphQLDirective>,
schema: GraphQLSchema,
context: mixed
) => voidThe ForEachOfResolver function definition is about a function that takes a callback that receives the type object, the type object name, an array of directives if any, the schema and any supplied context to use in the iteration and looping over the types defined in the Schematas schema representation. If an internal store of a schema is not availabe, one is created
ResolverArgs ✯
export type ResolverArgs = {
source: mixed,
args: mixed,
context: mixed,
info: GraphQLResolveInfo,
}Resolver functions receive four arguments when they execute. This type represents and object with the same four argument types within. Used by ResolverArgsTransformer and, indirectly by, MergeOptionsConfig objects.
ResolverArgsTransformer ✯
export type ResolverArgsTransformer = (args: ResolverArgs) => ResolverArgsA function that takes a ResolverArgs object and returns a ResolverArgs
ResolverMap ✯
export type ResolverMap = { [string]: Function | ResolverMap }A resolver map is either a flat string/Function mapping or nested version of itself. Each string key should either point to a object map of string/Function pairs or Function.
SchemaSource ✯
type SchemaSource = string | Source | Schemata | GraphQLSchema | ASTNodesA flow type that represents the various types of inputs that can often be used to construct an instance of Schemata.
MergeOptionsConfig ✯
export type MergeOptionsConfig = {
resolverInjectors: ResolverArgsTransformer | Array<ResolverArgsTransformer>,
}A MergeOptionsConfig is a way to configure the arguments that are bound to each resolver during a call to mergeSchema. The functions set as resolverInjectors can be either a single function or an array of functions.
UnionMergeResolver ✯
export type UnionMergeResolver = (
leftType: ASTNode,
leftUnion: NamedTypeNode,
rightType: ASTNode,
rightUnion: NamedTypeNode
) => NamedTypeNodeThe UnionMergeResolver is a function that takes both left and right types as well as left and right union types. The function decides which union type to return and does so.
ScalarMergeResolver ✯
export type ScalarMergeResolver = (
leftScalar: ScalarTypeDefinitionNode,
leftConfig: GraphQLScalarTypeConfig,
rightScalar: ScalarTypeDefinitionNode,
rightConfig: GraphQLScalarTypeConfig
) => GraphQLScalarTypeConfigThe ScalarMergeResolver is a function that takes both left and right scalars that are colliding and their custom resolver configs if available. Scalars definitions are a not always available, but if they are they will be provided. Therefore the default resolver for custom scalars will take whichever GraphQLScalarTypeConfig object is availabe starting with right, moving to left and then null.
External Functions ✯
isRootType() ✯
const isRootType = t => booleanGiven a AST type node from parsed SDL, return true if the type represents a root type; i.e. Query, Mutation or Subscription
normalizeSource() ✯
export function normalizeSource(
typeDefs: string | Source | Schemata | GraphQLSchema | ASTNode,
wrap: boolean = false
): (string | Schemata)A function that takes the various types of input that Schemata takes as a constructor and converts the results into either a string or Schemata instance if wrap is set to true.
runInjectors() ✯
export function runInjectors(
config: MergeOptionsConfig,
resolverArgs: ResolverArgs
): ResolverArgsGiven an initial set of arguments passed to a resolver function and a MergeOptionsConfig setup, it generates a new set of ResolverArgs that, post-modification, can finally be passed to the resolver when wrapped.
SchemaInjectorConfig() ✯
export function SchemaInjectorConfig(
schema: GraphQLSchema,
extraConfig?: MergeOptionsConfig
): MergeOptionsConfigThe merge options config takes the arguments passed into a given resolve() function, allowing the implementor to modify the values before passing them back out.
This function takes a schema to inject into the info object, or fourth parameter, passed to any resolver. Any extraConfig object added in will have its resolverInjectors added to the list to be processed.
stripResolversFromSchema() ✯
export function stripResolversFromSchema(
schema: GraphQLSchema
): ?ResolverMapWalk the supplied GraphQLSchema instance and retrieve the resolvers stored on it. These values are then returned with a [typeName][fieldName] pathing
Default Functions ✯
DefaultDirectiveMergeResolver() ✯
function DefaultDirectiveMergeResolver(
leftType: ASTNode,
leftDirective: DirectiveNode,
rightType: ASTNode,
rightDirective: DirectiveNode
): DirectiveNodeThe default directive resolver blindly takes returns the right field. This resolver is used when one is not specified.
DefaultAsyncEntryInspector() ✯
const DefaultAsyncEntryInspector: AsyncEntryInspector = (key, value, path, map) => ?Promise<{
[string]: Function
}>A default implementation of the EntryInspector type for use as a default to asyncWalkResolverMap. While not immediately useful, a default implementation causes asyncWalkResolverMap to wrap any non-function and non-object values with a function that returns the non-compliant value and therefore has some intrinsic value.
DefaultEntryInspector() ✯
const DefaultEntryInspector: EntryInspector = (key, value, path, map) => ?{
[string]: Function
}A default implementation of the EntryInspector type for use as a default to walkResolverMap. While not immediately useful, a default implementation causes walkResolverMap to wrap any non-function and non-object values with a function that returns the non-compliant value and therefore has some intrinsic value.
DefaultEnumMergeResolver() ✯
function DefaultEnumMergeResolver(
leftType: ASTNode,
leftValue: EnumValueNode,
rightType: ASTNode,
rightValue: EnumValueNode
): EnumValueNodeThe default field resolver blindly takes returns the right field. This resolver is used when one is not specified.
DefaultFieldMergeResolver() ✯
function DefaultFieldMergeResolver(
leftType: ASTNode,
leftField: FieldNode,
rightType: ASTNode,
rightField: FieldNode
): FieldNodeThe default field resolver blindly takes returns the right field. This resolver is used when one is not specified.
DefaultScalarMergeResolver() ✯
function DefaultScalarMergeResolver(
leftScalar: ScalarTypeDefinitionNode,
leftConfig: GraphQLScalarTypeConfig,
rightScalar: ScalarTypeDefinitionNode,
rightConfig: GraphQLScalarTypeConfig
): GraphQLScalarTypeConfigThe default scalar merge resolver returns the right config when there is one, otherwise the left one or null will be the default result. This is slightly different behavior since resolvers for scalars are not always available.
DefaultUnionMergeResolver() ✯
function DefaultUnionMergeResolver(
leftType: ASTNode,
leftUnion: NamedTypeNode,
rightType: ASTNode,
rightUnion: NamedTypeNode
): NamedTypeNodeThe default union resolver blindly takes returns the right type. This resolver is used when one is not specified.
Additional Goodies ✯
at() ✯
function at(
object: Object,
path: string | Array<string>,
setTo?: mixed,
playNice?: boolean = false
): mixedThis function takes an array of values that are used with eval to dynamically, and programmatically, access the value of an object in a nested fashion. It can take either a string with values separated by periods (including array indices as numbers) or an array equivalent were .split('.') to have been called on said string.
Examples:
// Calling `at` with either set of arguments below results in the same
// values.
let object = { cats: [{ name: 'Sally' }, { name: 'Rose' }] }
at(object, 'cats.1.name') => Rose
at(object, ['cats', 1, 'name']) => Rose
// Values can be altered using the same notation
at(object, 'cats.1.name', 'Brie') => Brie
// Values cannot normally be accessed beyond existence. The following
// will throw an error. A message to console.error will be written showing
// the attempted path before the error is again rethrown
at(object, 'I.do.not.exist') => ERROR
// However, if you want the function to play nice, `undefined` can be
// returned instead of throwing an error if true is specified as the
// fourth parameter
at(object, 'I.do.not.exist', undefined, true) => undefinedatNicely() ✯
function atNicely(
object: Object,
path: string | Array<string>,
setTo?: mixed
): mixedatNicely() is a shorthand version of calling at() but specifying true for the argument playNice. This can make reads normally performed with calls to at() where you want to prevent errors from being thrown with invalid paths
gql() ✯
function gql(template, ...substitutions)The gql template tag function takes the contents of a given template string and returns a Schemata instance using the string as a parameter to the constructor
// i.e.
let sdl = gql`
type Person {
name: String
}
`
console.log(sdl instanceof Schemata) // trueregister() ✯
function register(extension = '.graphql')This function takes an optional string denoting the extension string in which you store your SDL typeDefs in your project. Usually these are .graphql files. Regardless, once this function is invoked, you may then on in your project call require('/path/to/my/typeDef.graphql') and the resulting value would be an object with the following keys and values:
Named Exports
- astNode the AST document for the contents of the typeDefs.graphql file
- resolvers if there is an adjacent file with a .js extension and the same name, an attempt to load its contents as a module will occur. If successful this object will be the results of that
require() - schema an instance of
GraphQLSchemabased on the contents of the.graphqlfile - schemata a
Schematainstance built around the SDL, and JS resolvers if any - typeDefs an alias for schemata
The default export is the same as the schemata export
walkResolverMap() ✯
function walkResolverMap(
object: ResolverMap,
inspector: EntryInspector = DefaultEntryInspector,
wrap: boolean = true,
path: Array<string> = []
): ResolverMapGiven a ResolverMap object, walk its properties and allow execution with each key, value pair. If the supplied function for handling a given entry returns null instead of an object with the format {key: value} then that entry will not be included in the final output.
Paths here are somewhat interesting and bear a moments discussion. A path of ['job', 'responsibilities'] would indicate that the entry in question is located at object.job.responsibilities[entry.key].
- object an object containing string keys mapping to functions or objects nesting the same
- *inspector
2 years ago