Skip to content

Instantly share code, notes, and snippets.

@mnylen
Last active April 23, 2021 21:17
Show Gist options
  • Star 21 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mnylen/e944cd4e3255f4421a0b to your computer and use it in GitHub Desktop.
Save mnylen/e944cd4e3255f4421a0b to your computer and use it in GitHub Desktop.
Debounced fetching to reduce number of requests when doing API proxying through GraphQL

Simple implementation of debounced fetching in GraphQL to allow merging of multiple rest / database requests into one. Although this example uses GraphQL, the debouncedFetch / fetchProgramPlaycount implementations could probably be used in any context to achieve the same result.

This approach was first described by @leebyron at graphql/graphql-js#19 (comment)

For example this allows turning ten requests for playcounts from this GraphQL query into just one:

{
  latestPrograms(first: 10) {
    name,
    playcount
  }
}

Instead of having to do ten separate requests for

  • /playcounts?program_id=program1Id
  • /playcounts?program_id=program2Id
  • ...

to resolve the query, the debounced version does just one:

/playcounts?program_id=program1Id,program2Id,...

Also, during load multiple user requests can be combined into single request to backend api.

/* this function just returns a function you can use to queue items
the queueing returns a Promise you can return from GraphQL resolve()
let myDebouncedFetch = debouncedFetch((queue) => {
// do something with the queue which
// is an array of 3-tuples: [item, resolveFn, rejectFn]
// for example, merge the items into single rest call
})
myDebouncedFetch(item1).then(console.log)
myDebouncedFetch(item2).then(console.log)
*/
export default function(handlerFn) {
let queue = []
function processQueue() {
let toProcess = queue.splice(0, queue.length)
if (toProcess.length > 0) {
handlerFn(toProcess)
}
}
return function(item) {
return new Promise((resolve, reject) => {
if (queue.length === 0) {
process.nextTick(processQueue)
}
queue.push([item, resolve, reject])
})
}
}
import debouncedFetch from './debouncedFetch'
import fetch from 'node-fetch'
let fetchProgramPlaycount = debouncedFetch((queue) => {
let ids = queue.map(([program]) => program.id)
let url = "http://some.api.com/playcounts/?program_id=" + ids.join(",")
fetch(url).then((response) => response.json()).then((response) => {
let dataById = groupBy(response.data, 'id')
queue.forEach(([program, resolve]) => {
let idData = dataById[program.id]
resolve(idData ? idData[0].playcount : 0)
})
})
})
let ProgramType = new GraphQLObjectType({
name: 'Program',
fields: {
...,
playcount: { type: GraphQLInt, resolve: (program) => fetchProgramPlaycount(program) },
...
}
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment