Productivity
E-Doc
Document workspace with durable background processing.
- Year
- 2024
- Role
- Sole engineer
- Type
- Productivity
What it is
E-Doc is a document workspace: users sign in, upload documents, and the application processes them and reports on what it found. The reason it is in this portfolio is not the document handling — it is that the slow work is modelled as durable background jobs rather than something attempted inside a request, and that the results are surfaced as dashboards rather than left in a database.
By the numbers
- 4
- External services wired
- 01UploadUploadThing
- 02RecordPrisma
- 03EnqueueInngest event
- 04Processretryable steps
- 05Aggregateusage data
- 06ChartRecharts
Why the work does not happen in the request
Document processing is slow and it fails for boring reasons — a large file, a rate-limited third party, a cold start that runs past the timeout. Doing that work inside the HTTP request means the user watches a spinner and then gets an error that loses everything the job had already done.
Moving it to a job queue changes the failure mode. The upload succeeds immediately, the processing becomes a sequence of steps that can be retried independently, and a transient failure in step four does not throw away steps one through three.
What I built
Uploads go through UploadThing and are recorded in PostgreSQL through Prisma, at which point the request is done and the user gets their page back. That write emits an Inngest event, and the processing runs as durable steps outside the request lifecycle, with retries handled by the platform rather than by a loop I wrote.
Authentication is Clerk. The processed results are aggregated and rendered as Recharts dashboards, so the output of all that background work is something the user can actually read rather than a status column that says `complete`.
Making asynchronous work feel intentional
The honest difficulty with background processing is that it moves the problem into the interface. The user uploaded something and now nothing appears to be happening, which feels broken even though it is working exactly as designed.
So the queue has to be visible. Showing which documents are pending, which finished and which failed turns an invisible system into an understandable one, and it is the difference between a user waiting patiently and a user uploading the same file four times.
Decisions
Four calls I would make the same way again
01
Durable steps over fire-and-forget
Inngest gives each step its own retry and its own record of having run. A plain background promise gives you neither, and silently drops work whenever the function instance goes away.
02
Make steps idempotent
Anything that can be retried will eventually run twice. Writing each step so a second run is harmless is the only way retries are actually safe rather than merely available.
03
Show the queue to the user
Pending, complete and failed are states the user needs to see. Hiding them is what makes asynchronous software feel unreliable even when it is working.
04
Clerk rather than hand-rolled sessions
Authentication is the wrong place to be original. Using a provider meant the interesting work stayed on the processing pipeline where the actual problem was.
Stack
Background work
- Inngest
- Retryable steps
- Event-driven
Data
- PostgreSQL
- Prisma
- UploadThing
Interface
- Next.js 14
- Recharts
- Sass
- Clerk
Next case study
UXGear