I like the Universal Relay Boilerplate - in general I can tell they were very thoughtful about how they put this whole thing together, unlike most boilerplates for which folder organization and so on seems to be an intended afterthought (I guess because you won't do anything important at first, or something)... but not URB. Or at least we are picky about the same things.
Why do the same annoying thing...
...except for one thing: I don't get why they do this.
// Class used by GraphQL Server
export default class User
{
constructor( fields )
{
this.id = fields.id
this.User_AccountName = fields.User_AccountName
this.User_AccountPassword = fields.User_AccountPassword
this.User_DisplayName = fields.User_DisplayName
this.User_ProfilePhoto = fields.User_ProfilePhoto
this.User_Email = fields.User_Email
this.User_PhoneNumberMobile = fields.User_PhoneNumberMobile
this.User_Locale = fields.User_Locale
this.UserToken2 = fields.UserToken2
}
}
...two times in close proximity with no explanation?
Here is the "Type" which is actually a bit different than the original by virtue of an alias.
export default new GraphQLObjectType( {
name: 'Viewer',
interfaces: [NodeInterface],
isTypeOf: object => object instanceof User,
fields: {
id: globalIdField('Viewer'),
User_IsAnonymous: { type: GraphQLBoolean, resolve: (obj) => obj.id.equals( Uuid_0 ) },
User_AccountName: { type: GraphQLString, resolve: (obj) => obj.User_AccountName },
User_DisplayName: { type: GraphQLString, resolve: (obj) => obj.User_DisplayName },
User_ProfilePhoto: { type: GraphQLString, resolve: (obj) => obj.User_ProfilePhoto },
User_Email: { type: GraphQLString, resolve: (obj) => obj.User_Email },
User_PhoneNumberMobile: { type: GraphQLString, resolve: (obj) => obj.User_PhoneNumberMobile },
User_Locale: { type: GraphQLString, resolve: (obj) => obj.User_Locale },
UserToken2: { type: GraphQLString, resolve: (obj) => obj.UserToken2 },
..._ViewerFields,
},
} );
They intend it obviously
This is the most dramatic example of difference between model
and type
I could find in the boilerplate; the others are identical. I was actually a bit annoyed to find that no other guide recommends or even mentions doing a model.js
but rather starts from a type.js
.
I could understand if
- Some types need to differ from models for security, performance, aesthetic, design, etc
- Some types span models deliberately
- Some types encapsulate models for design reasons
But they do it everywhere which leads me to think I am missing something important here.
It could simply be that the boilerplate doesn't demonstrate the benefits of this approach. I think you already have some ideas where it could be beneficial, for example when your backend data model isn't exactly mapped to your GraphQL API.
A lot of organizations adopting GraphQL today have some existing backends and data models that they are used to working with. In this case, you can think of GraphQL as just another API layer, that sits next to your REST API or anything else. GraphQL is not your actual backend - you shouldn't be handling your business logic directly in the resolvers, because then your whole app will become coupled to GraphQL. This isn't as clear when you are building a new app from scratch with GraphQL, but it's a good pattern to follow because you will probably want to be flexible with your API in the future.
In the diagram above, GraphQL would be the "external interface." Note that it sits on top of the business logic/model layer, but doesn't replace it.
The types in your GraphQL API are the data you want to display in the UI. This could contain computed fields that aren't in the database, some fields could be pulling from multiple backends, etc.
Read about these concepts as presented by Dan Schafer in my summary of his React Europe talk here: https://medium.com/apollo-stack/graphql-at-facebook-by-dan-schafer-38d65ef075af#.awusd3ibv
They are not the same thing. Just the same name: