- #495: Added support for GraphQL v16_6_0. There were a number of breaking changes introduced in GraphQL v16 and these impacted a large number of tests and bit of the code. This provides fixes for those breaking changes. It also drops support for Node versions 10 - 13 but adds support for Node v16.
- Fix
alwaysFetch
's type #465
- Dependabot bumps
- #425: Add support for resolving GraphQL scalars backed by
sqlTable
s. Usually, scalars point to table columns, but this allows them to use the sameextensions
property to declare that the scalar is a whole table. This is often paired with aresolve
function that takes whatjoin-monster
returns and turns it into a valid value for the scalar. An example would be a tags field that outputs a list of strings, but where each tag is actually stored as it's own row in a different table in the database, or backing aJSONScalar
by a table to get around GraphQL's type strictness.
export const Tag = new GraphQLScalarType({
name: 'Tag',
extensions: {
joinMonster: {
sqlTable: 'tags',
uniqueKey: 'id',
alwaysFetch: ['id', 'tag', 'tag_order']
}
},
parseValue: String,
serialize: String,
parseLiteral(ast) {
// ...
}
})
- Added sqlDefaultPageSizeLimit and sqlPageLimit field specs to allow user to configure a default page limit and maximum page size limit for paginated fields.
const Post = new GraphQLObjectType({
// ...
fields: () => ({
// ...
only3Comments: {
type: new GraphQLList(Comment),
extensions: {
joinMonster: {
sqlPaginate: true,
sqlPageLimit: 100,
sqlDefaultPageSize: 5,
...
}
}
}
})
})
- Fix apollo-server null issue due to missing columns #444
Breaking changes:
- Update GraphQL requirement to version 15, which supports a new
extensions
property where join-monster config lives. The config keys and values are largely unchanged, but now they must be nested under anextensions: { joinMonster: ... }}
property on the GraphQLObjectTypes and fields using join-monster. To upgrade, you must move any non-standard keys off of yourGraphQLObjectType
s or field configs into theextensions
of the same field. So, something like this:
const User = new GraphQLObjectType({
name: 'User',
sqlTable: 'users',
uniqueKey: 'id',
fields: () => ({
id: {
type: GraphQLInt
},
email: {
type: GraphQLString,
sqlColumn: 'email_address'
},
})
}
becomes this:
const User = new GraphQLObjectType({
name: 'User',
extensions: {
joinMonster: {
sqlTable: 'users',
uniqueKey: 'id'
}
},
fields: () => ({
id: {
type: GraphQLInt
},
email: {
type: GraphQLString,
extensions: {
joinMonster: {
sqlColumn: 'email_address'
}
}
}
})
}
The resulting code is sadly more verbose, but the only supported way of layering in extra information to a GraphQL schema going forward, and safer in the presence of other GraphQL schema extensions.
Note: There are two configuration keys which have changed beyond just becoming nested in the extensions
property:
jmIgnoreAll
has been renamed toignoreAll
jmIgnoreTable
has been renamed toignoreTable
The old names for these configuration options will no longer work so please be sure to update.
New features:
- Add support for explicit
orderBy
column orderings by passing an array of{ column, direction }
objects. Useful for dynamically generatingorderBy
s without relying on object insertion order. Example:
const User = new GraphQLObjectType({
fields: () => ({
comments: {
type: new GraphQLList(Comment),
extensions: {
joinMonster: {
// order these alphabetically, then by "id" if the comment body is the same
orderBy: [
{ column: 'body', direction: 'asc' },
{ column: 'id', direction: 'desc' }
],
sqlJoin: (userTable, commentTable, args) =>
`${userTable}.id = ${commentTable}.author_id`
}
}
}
})
})
- Similarly, add support for explicit
sortKey
column orderings by passing an array of{ column, direction }
objects. Useful for dynamically generatingsortKey
s for connection pagination without relying on object insertion order. Example:
const User = new GraphQLObjectType({
fields: () => ({
posts: {
description: 'A list of Posts the user has written',
type: PostConnection,
args: connectionArgs,
extensions: {
joinMonster: {
sqlPaginate: true,
sortKey: [
{ column: 'created_at', direction: 'desc' },
{ column: 'id', order: 'desc' }
],
sqlJoin: (userTable, postTable) =>
`${userTable}.id = ${postTable}.author_id`
}
}
}
})
})
The old sortKey
synax (as an object looking like {key, order}
) continues to work but is no longer recommended. The new syntax allows for reliable sortKey ordering as well as independent directions per sort key, with all the complicated SQL generation handled for you.
- Connections inside union fragments #407
- Updated vulnerable version of lodash (
eed0264
)
- Numerous bug fixes
- TypeScript type definitions
- New 'mysql8' dialect which supports some pagination
- Don't write to debug module unless it's actually enabled.
- Properly format instances of Buffer.
- Support duplicate fields without aliases off the query root type.
- Add SQL AST node to sqlJoin callback signature.
- Remove the use of
Proxy
to improve compatibility.
- Add option for custom dialect modules.
- Various bug fixes.
New features:
LIMIT
functionality, supported on all fields.- Fetch columns from junction tables.
- For fields with junctions, you can now specify
WHERE
andORDER BY
clauses on the junction table or the main table, including paginated fields. - Ability to dynamically choose pagination implementation per-request.
- Better ability to write
where
functions that depend on args and info from the parent/ancestors.
Breaking changes:
- Fields with junctions have a new interface in order to support the new features.
- Any
where
,orderBy
, andsortKey
on many-to-many paginated fields used to be applied to the junction table. This has changed, and will be applied to the main table instead in order to be consistent with non-paginated junctions. If the old behavior is desired, you can nest those properties inside thejunction
object, which is part of the new API. - Change 4th parameter of
where
andsqlExpr
to the field's SQL AST Node, which is a lot more useful.
// this...
{
type: new GraphQLList(User),
junctionTable: 'relationships',
sqlJoins: [
(followers, relations) => `${followers}.id = ${relations}.follower_id`,
(relations, followees) => `${relations}.followee_id = ${followees}.id`
]
}
// is now this...
{
type: new GraphQLList(User),
junction: {
sqlTable: 'relationships',
sqlJoins: [
(followers, relations) => `${followers}.id = ${relations}.follower_id`,
(relations, followees) => `${relations}.followee_id = ${followees}.id`
]
}
}
// this...
{
type: new GraphQLList(User),
junctionTable: 'relationships',
junctionTableKey: [ 'follower_id', 'followee_id' ],
junctionBatch: {
thisKey: 'follower_id',
parentKey: 'id',
sqlJoin: (relations, followees) => `${relations}.followee_id = ${followees}.id`
}
}
// is now this...
{
type: new GraphQLList(User),
junction: {
sqlTable: 'relationships',
uniqueKey: [ 'follower_id', 'followee_id' ],
sqlBatch: {
thisKey: 'follower_id',
parentKey: 'id',
sqlJoin: (relations, followees) => `${relations}.followee_id = ${followees}.id`
}
}
}
// this...
{
type: UserConnection,
args: forwardConnectionArgs,
sqlPaginate: true,
orderBy: {
created_at: 'DESC',
followee_id: 'ASC'
},
junctionTable: 'relationships',
sqlJoins: [
(followers, relations) => `${followers}.id = ${relations}.follower_id`,
(relations, followees) => `${relations}.followee_id = ${followees}.id`
]
}
// is now this...
{
type: UserConnection,
args: forwardConnectionArgs,
sqlPaginate: true,
junction: {
sqlTable: 'relationships',
sqlJoins: [
(followers, relations) => `${followers}.id = ${relations}.follower_id`,
(relations, followees) => `${relations}.followee_id = ${followees}.id`
],
// the order now goes inside the `junction` if you want to sort on the junction table
orderBy: {
created_at: 'DESC',
followee_id: 'ASC'
}
}
// or you could apply the order on the user table by putting it out here
//orderBy: {
// created_at: 'DESC',
// id: 'ASC'
//}
// you could also place a `where` at either
}
- Add
jmIgnoreAll
andjmIgnoreTable
. - Make
sqlTable
a thunk. - Bug fix with recursively nested union and interface type fragments.
- Bug fix with for batch on a single-type parent.
- Add an API for GraphQLInterfaceType
- Add an API for GraphQLUnionType
- Add Oracle as supported dialect.
- Add
ORDER BY
support for non-paginated fields.
- Batching capabilities added.
- MariaDB can do pagination on batches.
sqlExpr
can now be asynchronous.- Remove unecessary "AS" from table alias in generated SQL.
Breaking changes:
getSQL
method removed. No longer makes sense in he new multiple-query paradigm.- Offset pagination adds the
total
to the connection object instead of thepageInfo
.
Deprecated:
joinTable
is deprecated. It was renamed tojunctionTable
to avoid over-use of the word "join".'standard'
dialect is deprecated because nothing really implements the standard. The new default is'sqlite3'
.
- Bug fixes with recursive fragments and argument parsing.
- Add
context
to thesqlJoin
parameters. - Support async in
sqlJoin
.
- Expose parent table aliases to
where
function.
- Fix bug for Postgres where
CONCAT
returns''
instead ofNULL
.
- Expose GraphQL args to
sqlJoin
function.
- Add support for fragments on interface types.
- Fix bug when composite keys contain timestamps or dates in PG dialect.
- Patch SQL injection risk.
- More automatic fetching using
getNode
implemented.
- Expose the
getSQL
method for getting only the converted SQL.
- Introducing raw SQL expressions for computed columns.
- Support asynchronicity in the
where
function.
- Add null check to node interface handler.
- Fix bug with
WHERE
conditions on paginated fields.
- Fix bug with query variables on the Node interface.
- Add support for dynamic sort keys on paginated fields. Sort keys can now be functions that receive the GraphQL arguments.
- Add support for query variables.
- Relay connection type names are no longer required to end with "Connection".
- Fix problem with introspection queries.
- Add dialect for MySQL/MariaDB.
- Fix bug with de-duplication of objects.
- Add Postgres dialect option.
- Support SQL pagination based on integer offsets.
- Support SQL pagination based on a sort key(s).
- Fix bug with nested fragments.
- Option to minify the raw data column names.
- Add test coverage tools.
- Add helper method for getting data for Relay's Node type.
- Fix bug with Union and Interface types.
- Add support for specifying schema names for your SQL tables.
- Detect Relay connection type and fetch data for it.
- Unique keys required for every table. Necessary for achieving good performance during object shaping/nesting.
- Composite keys supported for the unique key.