Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to split schemas into separate files? #43

Closed
wuzhuzhu opened this issue Jul 4, 2016 · 21 comments
Closed

How to split schemas into separate files? #43

wuzhuzhu opened this issue Jul 4, 2016 · 21 comments

Comments

@wuzhuzhu
Copy link

wuzhuzhu commented Jul 4, 2016

userSchema: (author schema like the same)

export const schema = [`
type Email {
address: String
verified: Boolean
}

type User {
emails: [Email]
username: String
randomString: String
}
`];

rootSchema:

import { schema as userSchema, resolvers as userResolver } from './user/schema';
const rootSchema = [`
type RootQuery {
author(name: String): Author
user: User
}

schema {
query: RootQuery
}
`]

export const schema = [...rootSchema, ...userSchema, ...authorSchema];


When I trying to contact schemas into 1 typedefinition in GitHunt way(seems its the only one example had separate schemas), I got this:
[ '\n type RootQuery {\n author(name: String): Author\n user: User\n }\n \n \n schema {\n query: RootQuery\n }\n', I20160704-11:08:53.149(8)? '\n type Email {\n address: String\n verified: Boolean\n }\n \n type User {\n emails: [Email]\n username: String\n randomString: String\n }\n', I20160704-11:08:53.149(8)? '\n# This uses the exact field names returned by the GitHub API for simplicity\ntype Author {\n name: String\n books: [Book]\n }\n \n type Book {\n name: String\n author: [Author]\n \n' ] { RootQuery: { user: [Function: user], author: [Function: author] }

Also I found the generateSchema in docs is deprecated. And I have no idea how to use the GraphQLSchema instance generated.

@helfer
Copy link
Contributor

helfer commented Jul 4, 2016

Yes, this is not very well documented at the moment. A type definition can contain other imported type definitions. If they are circular, you can use a function that returns an array as type definition. You should use the makeExecutableSchema function from graphql-tools (not apollo-server) here: https://github.com/apollostack/graphql-tools/blob/88eadb99a11733fb11726df539d6fe1bb3be9a6f/src/schemaGenerator.js#L67

We're planning on making a big push for documentation and content starting next week, so this should be more well-documented soon!

@wuzhuzhu
Copy link
Author

wuzhuzhu commented Jul 4, 2016

Thanks for answer.
When I trying to use makeExecutableSchema, I still got error when using concated typeDefs:
Am i miss something when doing this?
export const schema = [...rootSchema, ...userSchema, ...authorSchema];

/Users/walter/.meteor/packages/meteor-tool/.1.3.4_1.mi2rro++os.osx.x86_64+web.browser+web.cordova/mt-os.osx.x86_64/dev_bundle/server-lib/node_modules/fibers/future.js:280
W20160704-14:55:00.879(8)? (STDERR) throw(ex);
W20160704-14:55:00.879(8)? (STDERR) ^
W20160704-14:55:00.879(8)? (STDERR) Syntax Error GraphQL (27:21) Expected Name, found EOF
W20160704-14:55:00.880(8)? (STDERR)
W20160704-14:55:00.880(8)? (STDERR) 26: name: String
W20160704-14:55:00.880(8)? (STDERR) 27: author: [Author]

@helfer
Copy link
Contributor

helfer commented Jul 4, 2016

That should work just fine. Are you sure you don't have a syntax error in your file, like a missing } after [Author]?

@wuzhuzhu
Copy link
Author

wuzhuzhu commented Jul 4, 2016

Yes I fixed it by remove a comment line. Close.
thank you

@wuzhuzhu wuzhuzhu closed this as completed Jul 4, 2016
@icebob
Copy link

icebob commented Jul 6, 2016

@helfer could you show a sample, how to use the makeExecutableSchema function?

@helfer
Copy link
Contributor

helfer commented Jul 6, 2016

Yes, I'll write some documentation today or tomorrow, including an example.

@icebob
Copy link

icebob commented Jul 12, 2016

@helfer are you ready? Where can I find the docs & example?

@icebob
Copy link

icebob commented Jul 22, 2016

up

@dbx834
Copy link

dbx834 commented Jul 26, 2016

+1 for docs on merging schemas and resolvers

@helfer
Copy link
Contributor

helfer commented Jul 26, 2016

Sorry for the delay, we're going to do a major rewrite of the documentation soon, so this might actually take even longer. In the meantime, here's the function signature:

function makeExecutableSchema({
  typeDefs, // shorthand type definitions
  resolvers, // resolve function definitions
  connectors,
  logger, // logging function to be called for every resolver
  allowUndefinedInResolve = false,
  resolverValidationOptions = {},
})

The resolver validation options (and defaults) are as follows.

{
    requireResolversForArgs = true,
    requireResolversForNonScalar = true,
}

@icebob
Copy link

icebob commented Jul 28, 2016

@helfer Thanks, but unfortunately it's not solve my problem. My app is modularity, and in every module have queries in the Query. So I need to merge this queries to one before I gave it to the makeExecutableSchema.

For example:

post.module.js

const schema = `

type Query {
    posts(limit: Int, offset: Int, sort: String): [Post]
    post(id: Int, code: String): Post
}

type Post {
    id: Int!
    code: String!
    title: String
    content: String
    author: User!
}

type Mutation {
    upVote(postID: Int!): Post
    downVote(postID: Int!): Post
}

user.module.js


const schema = `
type Query {
    users(limit: Int, offset: Int, sort: String): [User]
    user(id: Int, code: String): User
}

type User {
    id: Int!
    code: String!
    fullName: String
    email: String
    username: String
    provider: String
    roles: [String]
    verified: Boolean
    gravatar: String
    lastLogin: Timestamp
    posts(limit: Int, offset: Int, sort: String): [Post]
}

type Mutation {
    createUser(userID Int!): User
    deleteUser(userID: Int!)
}
`

So I need to merge the Query definitions too. In schemaGenerator the concatenateTypeDefs functions concatenate the typeDefs only.

Are there any solution for this problem in the apollo-server or in the graphql-tools?

@helfer
Copy link
Contributor

helfer commented Jul 29, 2016

@icebob Yeah, that's not going to work, because you need to have unique names for everything.

If you want, you could probably write a mergeSchemas function which merges different schemas and even namespaces them. It's not that difficult, all you'd have to do is look into the document and produce a new merged one.

@dbx834
Copy link

dbx834 commented Jul 29, 2016

Here's a hacky inspiration,

File1.js,

// ----------------------------------------------------------------------- Schema & Resolvers

// ------------------------------------ Schema
const typeShard = `
type Planet {
  id: String
  name: String
  diameter: String
  gravity: String
  climate: String
  terrain: String
  rotationPeriod: String
  population: String
  orbitalPeriod: String
  surfaceWater: String
}
`;

const queryShard = `
planets: [Planet]
`;

// ------------------------------------ Resolvers
const resolvers = {
  Query: {
    planets() {
      return API.fetch();
    },
  },
};

// ----------------------------------------------------------------------- Exports

export { typeShard, queryShard, resolvers };

File2.js

// ----------------------------------------------------------------------- Schema & Resolvers

// ------------------------------------ Schema
const typeShard = `
type SomeOtherType {
  id: String
  ....
}
type RecordsAndMetaData {
  records: [SomeOtherType]
  totalRecords: String
  currentPage: String
}
`;

const queryShard = `
recordsAndMetaData(currentPage: String): RecordsAndMetaData
`;

// ------------------------------------ Resolvers
const resolvers = {
  Query: {
    recordsAndMetaData(root, { currentPage = '1' } = {}) {
      return API.configure({ currentPage });
    },
  },
  RecordsAndMetaData: {
    records() {
      return API.fetch();
    },
  },
};

// ----------------------------------------------------------------------- Exports

export { typeShard, queryShard, resolvers };

MergeAndCompileStuff.js

// ----------------------------------------------------------------------- Import stuff
import _ from 'lodash';

// ------------------------------------ Data Emulation
import { typeShard as ts1, queryShard as qs1, resolvers as rs1 } from './location/to/File1.js';
import { typeShard as ts2, queryShard as qs2, resolvers as rs2 } from './location/to/File2.js';

// ----------------------------------------------------------------------- Make stuff

// ------------------------------------ Compile schemas

const compiledSchema = [`
${ts1}
${ts2}
type Query {
  ${qs1}
  ${qs2}
}
schema {
  query: Query
}
`];

// ------------------------------------ Merge resolvers

const mergedResolvers = {};

_.map([rs1, rs2], function (resolverShard) {
  _.map(resolverShard, function (resolvers, outerKey) {

    if (mergedResolvers[outerKey] === undefined) {
      mergedResolvers[outerKey] = {};
    }

    _.map(resolvers, function (resolver, innerKey) {
      mergedResolvers[outerKey][innerKey] = resolver;
    });
  });
});

// ----------------------------------------------------------------------- Export stuff
export const thisPackage = 'sandbox:lib-transmit';
export { compiledSchema as schema, mergedResolvers as resolvers };

And in server/start.js,

// ----------------------------------------------------------------------- Imports

// ---------------------------------- Meteor & Apollo
import { Meteor } from 'meteor/meteor';
import { createApolloServer } from 'meteor/apollo';

// ---------------------------------- Fetch Schema & Resolvers for Apollo server
import { schema, resolvers } from 'meteor/sandbox:lib-transmit';

// ----------------------------------------------------------------------- Create Apollo Server
createApolloServer({
  graphiql: true,
  pretty: true,
  schema,
  resolvers,
});

// ----------------------------------------------------------------------- Startup code
Meteor.startup(() => {
  console.log('hello world!');
});

@icebob
Copy link

icebob commented Jul 29, 2016

@dbx834 thanks, your solution will be good.

@icebob
Copy link

icebob commented Jul 29, 2016

If there would be namespaces in GraphQL, it solves my problem.

Meanwhile this is my solution to merge schemas & resolvers (similar to @dbx834 solution): https://gist.github.com/icebob/553c1f9f1a9478d828bcb7a08d06790a

@veeramarni
Copy link

I was wondering, whether solution posted by @icebob is still valid in latest Apollo version?

@icebob
Copy link

icebob commented Dec 17, 2016

Yes, I'm using with new Apollo version.

@PierBover
Copy link

Has anyone worked out a more elegant solution to this problem other than joining everything by hand?

It's unrealistic to expect to have all types in the same file.

@vmatekole
Copy link

same inquiry as @PierBover here ...

@helfer
Copy link
Contributor

helfer commented Jun 16, 2017

@PierBover @vmatekole we're having a conversation about schema merging/joining over on another repo, please come join us! https://github.com/apollographql/graphql-prism/issues/1

@vmatekole
Copy link

Thanks @helfer — I will join the conv. Incidentally, I came across this and currently giving it a spin — https://github.com/okgrow/merge-graphql-schemas. I haven't looked under the hood but on first use it appears to fulfil basic merging reqs.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants