Use Join Monster’s SQL generation and query batching powers with the Apollo graphql-tools server package.
What’s this package for?
Suppose you have a GraphQL schema for a forum website, defined with the Schema Language like so:
const typeDefs = `
type Comment {
id: Int!,
body: String!,
postId: Int,
authorId: Int,
archived: Boolean
}
type Post {
id: Int!,
body: String!,
authorId: Int,
numComments: Int!,
comments: [Comment]
}
type User {
id: Int!,
email: String!,
fullName: String!,
favNums: [Int],
posts: [Post]
}
type Query {
user(id: Int!): User
}
`
module.exports = typeDefs
When using graphql-js, the reference implementation, you tag the Type constructors with extra metadata to configure Join Monster.
The schema language does not allow adding arbitrary properties to the type definitions.
This package let’s you add those tags without messing with the internals of the built schema object.
Once you familiarize yourself with Join Monster’s API, you can use all the same properties by passing it to this function.
const joinMonsterAdapt = require('join-monster-graphql-tools-adapter')
const typeDefs = require('../path/to/types')
const joinMonster = require('join-monster').default
const db = require('sqlite')
const { makeExecutableSchema } = require('graphql-tools')
const resolvers = {
Query: {
user(parent, args, ctx, resolveInfo) {
return joinMonster(resolveInfo, ctx, sql => {
return db.all(sql)
}, { dialect: 'sqlite3' })
}
},
User: {
fullName(user) {
return user.first_name + ' ' + user.last_name
}
}
}
const schema = makeExecutableSchema({
typeDefs,
resolvers
})
joinMonsterAdapt(schema, {
Query: {
fields: {
user: {
extensions: {
joinMonster: {
where: (table, args) => `${table}.id = ${args.id}`
}
}
}
}
},
User: {
extensions: {
joinMonster: {
sqlTable: 'accounts',
uniqueKey: 'id',
}
},
fields: {
email: {
extensions: {
joinMonster: {
sqlColumn: 'email_address'
}
}
},
fullName: {
extensions: {
joinMonster: {
sqlDeps: [ 'first_name', 'last_name' ]
}
}
},
posts: {
extensions: {
joinMonster: {
sqlJoin: (userTable, postTable) => `${userTable}.id = ${postTable}.author_id`,
}
}
}
}
},
Post: {
extensions: {
joinMonster: {
sqlTable: 'posts',
uniqueKey: 'id',
}
},
fields: {
numComments: {
extensions: {
joinMonster: {
sqlExpr: table => `(SELECT count(*) FROM comments where ${table}.id = comments.post_id)`
}
}
},
comments: {
extensions: {
joinMonster: {
sqlBatch: {
thisKey: 'post_id',
parentKey: 'id'
}
}
}
}
}
},
Comment: {
extensions: {
joinMonster: {
sqlTable: 'comments',
uniqueKey: 'id',
}
},
fields: {
postId: {
extensions: {
joinMonster: {
sqlColumn: 'post_id'
}
}
},
authorId: {
extensions: {
joinMonster: {
sqlColumn: 'author_id'
}
}
}
}
}
})
Now that our schema is Join-monsterized, we are ready to start executing some queries!
const { graphql } = require('graphql')
const query = `{
user(id: 1) {
id
fullName
email
posts {
id
body
numComments
comments {
id
body
authorId
archived
}
}
}
}`
graphql(schema, query).then(doSomethingCrazy)
Advisory
There is a known issue (see #4) that passing the logger in graphql-tools makeExecutableSchema breaks automatic fetching of default column values. For the time being, it is suggested to remove the logger or add sqlColumn to every field.
评论