Skip to content
Alice de Bernyfr
Back to résumé

Beedeez · 2020 – 2023

Bulk download of training certificates

Generating every learner's certificate for an account in the background, then delivering them as a single zip through polling.

  • TypeScript
  • Node.js
  • BullMQ
  • Redis
  • AWS S3
  • React Native
  • React Native Web

Context

At Beedeez, learners earned a certificate at the end of a training path, based on their answers to the various quizzes. These PDF certificates were generated on the server, and each learner could view and download theirs. Administrators, for their part, could download the certificates of all their users at once.

The system had to handle bulk certificate downloads. Generating everything within a single HTTP request was not an option: the request could time out, and a dropped connection midway would have thrown all the work away. On the backend, we also had to be able to generate and store a large number of certificates before sending them.

Approach

I separated the download request from the generation itself: the server starts the work in the background and responds right away, then the browser regularly asks how the export is going.

Download request

An administrator, from the back office

Browser

Polling starts↻

Asks for the export status at a regular interval

Response: “in progress”

Polling continues until the zip is ready

Server

BullMQ queueRedis

One job per batch of certificates

Workers×N

Generate each batch's certificates

Certificates on S3

Stored temporarily until everything is generated

Zip creation

Once every job has finished, also stored on S3

Response: “done”

The next poll receives a signed URL to the zip and stops

Zip download

Straight from S3, without going through our server

As soon as the request is made, the browser starts polling while the server generates the certificates in batches. Once the zip is ready, the response to the next poll carries the download link.

Backend: a BullMQ queue

When an administrator requested the download, the server added jobs to a BullMQ queue (backed by Redis), each job covering one batch of certificates. Splitting the work into batches avoided a single long, memory-hungry job for large accounts.

A worker then processed these jobs, generated the PDFs and stored them temporarily on AWS S3 while the rest were generated. Once every job had finished, the certificates were bundled into a zip, also uploaded to S3, and the export moved to a "completed" status.

Frontend: polling

On the frontend, I built a polling system in TypeScript. The client sent a request to the backend at a regular interval to check the export status. Once the zip was ready, the response contained a signed S3 URL. This temporary link made it possible to download the zip straight from S3, without routing it through our server. Polling stopped after a set delay (timeout), so the client would not query the server forever if an export got stuck.

Since generation runs on the server, independently of the client's connection, a network drop does not interrupt the work.

Outcome

Administrators could get the certificates of all their users in a single zip, without relying on one request that might time out. Splitting the work into batches improved performance when generating data in bulk.

Takeaways

  • As soon as a task may outlast an HTTP request, it is better to hand it to a queue and expose a status. The work then no longer depends on the client's connection.
  • To generate a large volume of files, splitting the work into batches improves performance.