Graphcool 1.0 - How can I split resolver definitions file?


#1

Hi,

Currently, we have the schema.graphql file that contains all resolver definitions (such as queries, mutations, and other types). My schema.graphql looks like this (we moved generated folder at the root path of our project):

# import Order from "./../generated/database.graphql"
# import User from "./../generated/database.graphql"
# import Address from "./../generated/database.graphql"
# import Review from "./../generated/database.graphql"
# import Payment from "./../generated/database.graphql"

type Query {
  me: User
  orders: [Order!]
}

type Mutation {
  signup(email: String!, password: String!, name: String!): AuthPayload!
  login(email: String!, password: String!): AuthPayload!
  orderDraft(userId: String!, dayId: String!, menuQuantities: [String], productQuantities: [String], addressId: String!): String
  confirmOrder(orderId: String!): String
  confirmUser(userId: String!, token: String!): AuthPayload
  modifyEmail(userId: String!, newEmail: String!): AuthPayload
  modifyPassword(userId: String!, password: String!, newPassword: String!): AuthPayload
}

type AuthPayload {
  token: String!
  user: User!
}

type Order {
  createdAt: DateTime!
  preorder: Boolean 
  address: Address!
  companyDiscount: Int 
  coupon: Coupon 
  creditUsed: Int 
  cutlery: Boolean
  invoice: String
  total: Int
  review: Review
  payment: [Payment!]! 
  day: Day! 
  state: OrderState
  cashConversion: Int
  creditsRefunded: Boolean
  stripeRefunded: Boolean
  slot: Slot!
  billingAddress: Address!
}

type User {
  addresses: [Address!]! 
  cashToTake: Int 
  company: Company 
  creditBalance: Int 
  firstname: String!
  lastname: String!
  orders: [Order!]! 
  phone: String!
  reviews: [Review!]! 
  sponsorshipCode: String 
  email: String
  primaryAddress: Address 
  countryCode: String!
}

How could I split this file? (otherwise it will get bigger and bigger and it will become complex to maintain)

I would like to have userResolvers.graphql, orderResolvers.graphql, etc


#2

You do not need to redefine your models if they are imported on top


#3

From what I’m seeing, you can just include all the files in the datamodel property of graphcool.yml (as a list), and it resolves all the dependencies without having to explicitly use # import. It would be good for the docs to explain this behavior (and to have documentation on # import period, as I haven’t seen that yet).


#4

@KTITS_Tom Oh thanks, good to know that!

@LFP6 Thanks for your response! From what I understand, the graphcoolconfig.yml file uses the src/schema.graphql file to define which resolvers will be accessible in my application. It would seem that graphcool expects .graphql files present in datamodel/ to be real entities and not just resolvers definition.

So you’re saying that it would be ok to include the src/schema.graphql file in the datamodel property of graphcool.yml while the file is expected in graphcoolconfig.yml?

Or maybe I misunderstood? Because I’m not trying to avoid import, but to have one file per resolver definition as I have for my types order, user, address, etc. :grinning:

Here is my graphcoolconfig.yml:

projects:
  app:
    schemaPath: "src/schema.graphql" <---- This file is the one I put in my first message
    extensions:
      endpoints:
        default: "http://localhost:4000"
  database:
    schemaPath: "generated/database.graphql"
    extensions:
      graphcool: graphcool.yml

Here is my graphcool.yml:

# the name for the service (will be part of the service's HTTP endpoint)
service: nestor-graph

# the cluster and stage the service is deployed to
stage: ${env:GRAPHCOOL_STAGE}
cluster: ${env:GRAPHCOOL_CLUSTER}

# to disable authentication:
# disableAuth: true
secret: ${env:GRAPHCOOL_SECRET}

# the file path pointing to your data model
datamodel: 
  - database/enums.graphql
  - database/address.graphql
  - database/administrator.graphql 
  - database/company.graphql
  - database/configuration.graphql 
  - database/coupon.graphql
  - database/credit.graphql 
  - database/day.graphql
  - database/deal.graphql 
  - database/domains.graphql
  - database/enums.graphql 
  - database/file.graphql
  - database/log.graphql 
  - database/menu.graphql
  - database/menuQuantity.graphql 
  - database/menuStock.graphql
  - database/order.graphql 
  - database/payment.graphql
  - database/place.graphql 
  - database/product.graphql
  - database/productQuantity.graphql 
  - database/productStock.graphql
  - database/restaurant.graphql 
  - database/review.graphql
  - database/slot.graphql
  - database/user.graphql
  - database/zip.graphql

And my folders are organized as follow:

database/
    file.graphql (our types)
    user.graphql
    order.graphql
    etc..
generated/
    database.graphql (auto generated)
src/
   schema.graphql (resolvers and public types definitions)

#5

Hey @Mael, that’s a great question!


Here’s a quick explanation of the different files that are involved:

  • database/graphcool.yml - defines service information, including the datamodel
  • generated/database.graphql - this is the database schema, which is automatically generated based on the datamodel and describes the GraphQL API exposed by your service. I’ve found calling it graphcool.graphql makes it simpler to work with this file.
  • src/schema.graphql - this is the application schema, defining the GraphQL API exposes to your client application. Using import statements you can refer to GraphQL types from other schema files, such as from your database schema.

So, your question is actually solely focused on how to split up the application schema into multiple files.

You can use the # import statement to accomplish this :slight_smile:

Simple example:

# src/mutations.graphql

# import User "./../generated/database.graphql"

type Mutation {
  signup(email: String!, password: String!, name: String!): AuthPayload!
  login(email: String!, password: String!): AuthPayload!
  # other mutations...
}

type AuthPayload {
  token: String!
  user: User!
}
# src/queries.graphql

# note that we can import multiple types in one line
# import User, Order "./../generated/database.graphql"

type Query {
  me: User
  orders: [Order!]
}
# src/schema.graphql

# import Query.* from 'queries.graphql'
# import Mutation.* from 'queries.graphql'

type Query {
  anotherQuery: String
}

The import syntax is further described in graphql-import, including some more examples :slightly_smiling_face:


#6

@Mael As @nilan pointed out, the graphcool.yml datamodel would be for the datamodel not the application schema - my bad, I read your question too fast and just jumped to the conclusion it was the same concern I had just resolved for myself.

Which makes me wonder - would it be valuable for the typedefs property of GraphQLServer to accept an array as opposed to using # imports, similarly to graphcool.yaml?


#7

@nilan Wow! It works! :tada:

Thank you very much for the clarification. I should have thought about it, now that I have the example in front of me it seems pretty obvious and logic. :sweat_smile: Now, I spread my resolvers in this way in my project:

src/
    user/
        resolvers.graphql
        resolvers.js
    order/
        resolvers.graphql
        resolvers.js

So my src/user/resolver.graphql looks like:

# import Address, Company, Order, Review from '../../generated/database.graphql'

type Query {
  me: User
}

type Mutation {
  signup(email: String!, password: String!, name: String!): AuthPayload!
  login(email: String!, password: String!): AuthPayload!
  confirmUser(userId: ID!, token: String!): AuthPayload
  modifyEmail(userId: ID!, newEmail: String!): User
  modifyPassword(userId: ID!, password: String!, newPassword: String!): User
}

type AuthPayload {
  token: String!
  user: User!
}

type User {
  addresses: [Address!]! 
  cashToTake: Int 
  company: Company 
  creditBalance: Int 
  firstname: String!
  lastname: String!
  orders: [Order!]! 
  phone: String!
  reviews: [Review!]! 
  sponsorshipCode: String 
  email: String
  primaryAddress: Address 
  countryCode: String!
}

However, I noticed that no error occurred if I do not import some schemas that I use in my user/resolvers.graphql. For example, if I remove Address from the import, then I do not notice any errors, while I ask for it on my user type. Same for others. On the other hand, if I ever remove them all, I have this error:

/Users/mael/repos/GraphManager/node_modules/graphql-yoga/node_modules/graphql-import/dist/definition.js:126
                    throw new Error("Field " + field.name.value + ": Couldn't find type " + typeName + " in any of the schemas.");
                    ^

Error: Field createdAt: Couldn't find type DateTime in any of the schemas.
    at /Users/mael/repos/GraphManager/node_modules/graphql-yoga/node_modules/graphql-import/dist/definition.js:126:27
    at Array.forEach (<anonymous>)
    at collectNewTypeDefinitions (/Users/mael/repos/GraphManager/node_modules/graphql-yoga/node_modules/graphql-import/dist/definition.js:105:30)
    at Object.completeDefinitionPool (/Users/mael/repos/GraphManager/node_modules/graphql-yoga/node_modules/graphql-import/dist/definition.js:22:41)
    at Object.importSchema (/Users/mael/repos/GraphManager/node_modules/graphql-yoga/node_modules/graphql-import/dist/index.js:85:41)
    at new GraphQLServer (/Users/mael/repos/GraphManager/node_modules/graphql-yoga/dist/src/index.js:88:45)
    at Object.<anonymous> (/Users/mael/repos/GraphManager/src/index.js:5:16)
    at Module._compile (module.js:641:30)
    at Object.Module._extensions..js (module.js:652:10)
    at Module.load (module.js:560:32)
    at tryModuleLoad (module.js:503:12)
    at Function.Module._load (module.js:495:3)
    at Function.Module.runMain (module.js:682:10)
    at startup (bootstrap_node.js:191:16)
    at bootstrap_node.js:613:3

This would mean that I do not necessarily need to import all the schemas I mention in my src/user /resolvers.graphql? But at least one?

@LFP6 No worries! Thank you for trying to help me. :relaxed: