Skip to content

Instantly share code, notes, and snippets.

@intrnl
Last active February 22, 2023 12:03
Show Gist options
  • Save intrnl/154fd41d6a7902bad88cf068d8f61860 to your computer and use it in GitHub Desktop.
Save intrnl/154fd41d6a7902bad88cf068d8f61860 to your computer and use it in GitHub Desktop.
Hasura v2 migration package for Apollo Client
// NOTE: Hasura v1 to Hasura v2 conversion
/* eslint-disable import/export */
import {
useQuery as useApolloQuery,
useSubscription as useApolloSubscription,
} from '@apollo/react-hooks-unpatched'
import gql from 'graphql-tag'
export * from '@apollo/react-hooks-unpatched'
const OPERATORS = [
'_in',
'_nin',
'_eq',
'_gt',
'_lt',
'_gte',
'_lte',
'_like',
'_ilike',
'_contains',
'_not',
'_and',
'_or',
'_is_null',
]
const NULL_RE = /\bnull\b/
const NULL_REPLACE_RE = new RegExp(
`\\b(${OPERATORS.join('|')}):\\s*null\\b`,
'g'
)
const removeEmpty = obj => {
const res = {}
for (const key in obj) {
const val = obj[key]
if (Array.isArray(val)) {
res[key] = val
} else if (typeof val === 'object') {
if (val !== null) {
const replaced = removeEmpty(val)
if (Object.keys(replaced).length > 0) {
res[key] = replaced
}
}
} else if (val !== undefined) {
res[key] = val
}
}
return res
}
const fixGqlDocument = (document, param) => {
const orig = document.loc.source.body
let modified = false
let str = orig
// Remove variable usage inside _eq or other equality conditions
const toPrune = []
// Replace all variable usage with an empty object {}
const toObject = []
for (const definition of document.definitions) {
for (const varDef of definition.variableDefinitions) {
const key = varDef.variable.name.value
const value = param && param[key]
if (varDef.type.kind === 'NonNullType') {
continue
}
if (value === null || value === undefined) {
if (
varDef.type.kind === 'NamedType' &&
varDef.type.name.value.endsWith('_comparison_exp')
) {
toObject.push(key)
} else {
toPrune.push(key)
}
}
}
}
if (toPrune.length > 0) {
const joined = toPrune.join('|')
const re = new RegExp(
`\\b(${OPERATORS.join('|')}):\\s*\\$(${joined})\\b`,
'g'
)
str = str.replace(re, '')
modified = true
}
if (toObject.length > 0) {
const joined = toObject.join('|')
const re = new RegExp(`:\\s*\\$(${joined})\\b`, 'g')
str = str.replace(re, ': {}')
modified = true
}
if (NULL_RE.test(str)) {
str = str.replace(NULL_REPLACE_RE, '')
modified = true
}
if (modified) {
try {
return gql(str)
} catch (error) {
if (process.env.NODE_ENV !== 'production') {
// eslint-disable-next-line no-console
console.log({original: orig, replaced: str, param})
}
throw new Error(`Bad GraphQL query!`)
}
}
return document
}
export const useQuery = (query, options = {}) => {
const {variables, unstable_skipPatch = false, ...rest} = options
let _query = query
let _variables = variables
if (!unstable_skipPatch) {
_variables = variables ? removeEmpty(variables) : undefined
_query = fixGqlDocument(query, _variables)
}
return useApolloQuery(_query, {
...rest,
variables: _variables,
})
}
export const useSubscription = (query, options = {}) => {
const {variables, unstable_skipPatch = false, ...rest} = options
let _query = query
let _variables = variables
if (!unstable_skipPatch) {
_variables = variables ? removeEmpty(variables) : undefined
_query = fixGqlDocument(query, _variables)
}
return useApolloSubscription(_query, {
...rest,
variables: _variables,
})
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment