Declarative and programmatic GraphQL
I am adopting GraphQL for new fullstack projects and I have already learned many concepts and started my first projects.
My question is related to using a declarative and graphical definition of a GraphQL schema. Basically everything I can see on the Official GraphQL site takes a declarative approach: you define the schema in one or more files, for example (thanks to this example here ):
type Brand {
name: String
logoUrl: String
}
enum Gender {
MALE
FEMALE
}
type Image {
thumbnailUrl: String
smallUrl: String
mediumUrl: String
largeUrl: String
}
type Article {
id: ID! # non-nullable, is guaranteed to exist on every article
name: String
thumbnailUrl: String
brand: Brand
genders: [Gender]
images: [Image]
recommendations: [Article]
}
type Query {
Article(id: ID!): Article
Articles: [Articles]
}
Very clean and concise code even for complex data structures.
But most of the examples I see on the internet, and even the books I have studied, take a programmatic approach to constructing the circuit, for example:
import { GraphQLObjectType, GraphQLInputObjectType } from 'graphql';
import {GraphQLNonNull, GraphQLID, GraphQLList } from 'graphql';
import { GraphQLString, GraphQLInt, GraphQLBoolean } from 'graphql';
import { UserType } from '../User/types';
import UserModel from '../../../models/User';
const fields = {
_id: {
type: new GraphQLNonNull(GraphQLID)
},
name: {
type: GraphQLString
},
phone: {
type: GraphQLString
}
};
const CompanyType = new GraphQLObjectType({
name: 'Company',
description: 'Company',
fields: fields
})
const Company = {
type: CompanyType,
description: 'Get single company',
args: {
id: {
name: 'id',
type: new GraphQLNonNull(GraphQLID)
}
},
resolve(root, params) {
params.deleted = false;
return CompanyModel.find(params).exec();
}
}
const Companies = {
type: new GraphQLList(CompanyType),
description: 'Get all companies',
resolve(root) {
const companies = CompanyModel.find({ deleted: false }).exec();
if (!companies) {
throw new Error('Error getting companies.')
}
return companies;
}
}
export default {
Company,
Companies
}
My goal is to build a large SaaS application, so the schema is going to be quite complex and I'm afraid the code will soon get complicated.
So, should I use a declarative approach, a programmatic approach, or a combination of the two?
What are the best practices here?
source to share
This section has a good discussion here and here .
IMHO, the biggest benefit to defining your schema in GraphQL schema language is readability. This makes your diagram easier to read and understand, especially for internal users who may request the endpoint but are not actually involved in developing it. I think this also makes the definition and modification of the schema less error prone.
On the other hand, defining a schema with software provides much more flexibility. For example, if you are using buildSchema
, you are limited to passing to resolvers only for your requests and mutations. This works great if you're fine with each type using only the default resolver, but what happens when you need to define converters for individual fields?
Defining a schema programmatically allows you to define converters for individual fields in any of the types you specify. Not only does this help transform the data you return from your database (by turning it thumbanail_url
into a field thumbnailUrl
), but if those fields require additional queries to the database, it prevents them from being disabled if the field is not actually there, which can be a significant performance boost. As the documentation points out, this approach is also useful if you want to automatically generate a schema.
Personally, I love graphql-tools
makeExecutableSchema . This is a kind of "middle class" approach that allows you to define your types very easily (using the GraphQL schema language) while still providing flexibility in implementing solutions.
source to share