Skip to content

Instantly share code, notes, and snippets.

@mnylen
Last active April 23, 2021 21:17
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