I would very much prefer to have my graphql schema defined via regular source code rather than strings.
Is this an explicitly supported use case ? If so are there any docs that show how to get from an array of "object type config" objects to a working schema that can be passed to buildSchema?
Is there a way to render a graphql schema from such a representation ?
Yes this is definitely possible. The documentation is here: http://graphql.org/graphql-js/type/
I also prefer to do it this way. We export each type created by GraphQLObjectType in separate files.
An example would be something like this:
const {
GraphQLSchema,
GraphQLObjectType,
GraphQLString
} = require('graphql');
const Schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Query',
fields: {
user: {
type: new GraphQLObjectType({
name: 'User',
fields: {
firstName: {type: GraphQLString},
lastName: {type: GraphQLString},
email: {type: GraphQLString}
}
}),
args: {
userId: {type: GraphQLString}
},
resolve(root, args, context, info) {
// look up and return user object
}
}
}
})
});
When you do this, you do not need the buildSchema function, the Schema object here is the same type that would be returned by buildSchema. You can convert this format to the Schema Language format using the printSchema function (documented here).
const {printSchema} = require('graphql');
console.log(printSchema(Schema)); // Schema defined above
Will output:
type Query {
user(userId: String): User
}
type User {
firstName: String
lastName: String
email: String
}
Awesome, thanks.
An alternative solution is to make a GraphQLSchemaBuilder, design your own class and use your own SchemaBuilder to build the schema string, then pass the string to buildSchema. The concept is like using knex to build SQL string and pass it to database.
here is my implementation:
import { makeExecutableSchema } from 'graphql-tools'
import { prepareSchema } from 'graphql-rxjs';
import _ from 'lodash'
import * as types from './types'
import * as scalarTypes from './types/scalars'
import * as enumTypes from './types/enums'
export class GraphQLBuilder {
constructor (modelManager, options = {}) {
this.modelManager = modelManager
this.typeClasses = []
.concat(_.toArray(scalarTypes))
.concat(_.toArray(enumTypes))
.concat(_.toArray(types))
this.logger = options.logger
}
loadType(typeClass) {
this.typeClasses.push(typeClass)
}
buildDefinitions () {
let definitions = {}
for (let typeClass of this.typeClasses) {
let definition = typeClass.definition
if (definition) {
definitions[typeClass.resolverName] = definition
}
}
let schemas = ['query:Query']
if (definitions['Mutation']) {
schemas.push('mutation:Mutation')
}
if (definitions['Subscription']) {
schemas.push('subscription:Subscription')
}
definitions['schema'] = `schema{${schemas.join(',')}}`
return _.toArray(definitions).join(',')
}
buildResolvers () {
let resolvers = {}
for (let typeClass of this.typeClasses) {
let typeResolvers = typeClass.getResolver(this.modelManager)
if (_.size(typeResolvers) > 0) {
resolvers[typeClass.resolverName] = typeResolvers
}
}
return resolvers
}
buildExecutableSchema () {
let definitions = this.buildDefinitions()
let resolvers = this.buildResolvers()
if (this.logger) {
this.logger.info('============ definitions ============')
this.logger.info(definitions)
this.logger.info('============ resolvers ============')
for (let resolverName in resolvers) {
let endpoints = _.keys(resolvers[resolverName])
this.logger.info(`${resolverName} (${endpoints.length}): (${endpoints.join(', ')})`)
}
}
let executableSchema = makeExecutableSchema({
typeDefs: definitions,
resolvers: resolvers
})
prepareSchema(executableSchema)
return executableSchema
}
}
Just wanted to note that not only is it possible, it's the library's preferred way to construct schema. buildSchema was added later as a convenience for those who prefer to rely on the schema definition language directly, but still wish to end up with a GraphQLSchema instance in code.
Thanks everybody, so doing it via js code works, how about declaring the schema in JSON in a way that's compatible between different language implementations ? For example in js in order to refer to an existing type from another type's/query definition it looks like I need to provide either an existing object reference or a lazyly evaluable function which implies custom de/hydration code if I were to persist them to json.
Thanks everybody, so doing it via js code works, how about declaring the schema in JSON in a way that's compatible between different language implementations ?
@graphsqrl You can use introspection for that.
This introspection query is de-facto standard and it's JSON result is compatible between implementations.
@graphsqrl in addition to Rob's comment, I would suggest taking a look at the code samples in nodejs-api-starter/src/schema.
Thanks, but running an introspection query requires an instance of a graphql schema and using a graphql-syntaxed query, is there a way to build directly from an authored json (not javascript !) document (maybe modified from a a generic introspection query result ?).
Basically I would like my single-source-of-truth for the graphql schema (including queries) to be entirely in json rather than grarphql syntax without rolling my own json schema and preprocessing it on each platform before programmatically building a schema.
@graphsqrl You can create result of introspection query manually and use buildClientSchema to create a schema from it.
However, declaring the schema in a way that's compatible between different language implementations is exactly why IDL/SDL syntax was created.
Can you describe why you can't use it in your case?
As @IvanGoncharov pointed out, the GraphQL SDL should be your tool of choice for representing a schema in an implementation-agnostic fashion. We're in the process of finalizing the specification for it, so hopefully soon most GraphQL implementations will support it natively.
If you wish to use JSON instead, you could either manually maintain an introspection result or manually maintain the result of parsing a GraphQL SDL, however I wouldn't recommend either of these since the dynamism and verbosity of JSON in this context makes a GraphQL schema difficult to read and write.
Is there some existing function to convert SDL and/or a GraphQLSchema object into introspection JSON?
My use-case is that I want to generate typescript types by having a JS script that transforms the introspection JSON into typescript code. I don't want to call the server since I already have a file with the SDL in. There are already tools to do codegen but I think this approach would be more flexible but perhaps it is not a good idea?
@jonaskello - yes this library can do both of those things. To convert SDL to a GraphQLSchema: buildSchema(schemaSDLText), to convert a GraphQLSchema to introspection JSON: introspectionFromSchema(schemaInstance)
There are a lot of other utilities for operating with a schema and using them to do interesting things like code-generation - check them all out here https://github.com/graphql/graphql-js/blob/master/src/index.js
Thanks, introspectionFromSchema was exactly what I was looking for :-).
Most helpful comment
Yes this is definitely possible. The documentation is here: http://graphql.org/graphql-js/type/
I also prefer to do it this way. We export each type created by GraphQLObjectType in separate files.
An example would be something like this:
When you do this, you do not need the
buildSchemafunction, theSchemaobject here is the same type that would be returned bybuildSchema. You can convert this format to the Schema Language format using theprintSchemafunction (documented here).Will output: