An assignment for building a reliable chunking setup that ensures recording data stays accurate in all cases — no data loss, no silent failures.
Client (Browser)
│
├── 1. Record & chunk data on the client side
├── 2. Store chunks in OPFS (Origin Private File System)
├── 3. Upload chunks to a storage bucket
├── 4. On success → acknowledge (ack) to the database
│
└── Recovery: if DB has ack but chunk is missing from bucket
└── Re-send from OPFS → bucket
Main objective: In all cases, the recording data stays accurate. OPFS acts as the durable client-side buffer — chunks are only cleared after the bucket and DB are both confirmed in sync.
- Client-side chunking — Recording data is split into chunks in the browser
- OPFS storage — Each chunk is persisted to the Origin Private File System before any network call, so nothing is lost if the tab closes or the network drops
- Bucket upload — Chunks are uploaded to a storage bucket (can be a local bucket for testing, e.g. MinIO or a local S3-compatible store)
- DB acknowledgment — Once the bucket confirms receipt, an ack record is written to the database
- Reconciliation — If the DB shows an ack but the chunk is missing from the bucket (e.g. bucket purge, replication lag), the client re-uploads from OPFS to restore consistency
- Next.js — Frontend (App Router)
- Hono — Backend API server
- Bun — Runtime
- Drizzle ORM + PostgreSQL — Database
- TailwindCSS + shadcn/ui — UI
- Turborepo — Monorepo build system
npm install- Make sure you have a PostgreSQL database set up.
- Update your
apps/server/.envwith your PostgreSQL connection details. - Apply the schema:
npm run db:pushnpm run dev- Web app: http://localhost:3001
- API server: http://localhost:3000
Target: 300,000 requests to validate the chunking pipeline under heavy load.
Use a load testing tool like k6, autocannon, or artillery to simulate concurrent chunk uploads.
Example with k6:
import http from "k6/http";
import { check } from "k6";
export const options = {
scenarios: {
chunk_uploads: {
executor: "constant-arrival-rate",
rate: 5000, // 5,000 req/s
timeUnit: "1s",
duration: "1m", // → 300K requests in 60s
preAllocatedVUs: 500,
maxVUs: 1000,
},
},
};
export default function () {
const payload = JSON.stringify({
chunkId: `chunk-${__VU}-${__ITER}`,
data: "x".repeat(1024), // 1KB dummy chunk
});
const res = http.post("http://localhost:3000/api/chunks/upload", payload, {
headers: { "Content-Type": "application/json" },
});
check(res, {
"status 200": (r) => r.status === 200,
});
}Run:
k6 run load-test.js- No data loss — every ack in the DB has a matching chunk in the bucket
- OPFS recovery — chunks survive client disconnects and can be re-uploaded
- Throughput — server handles sustained 5K req/s without dropping chunks
- Consistency — reconciliation catches and repairs any bucket/DB mismatches after the run
recoding-assignment/
├── apps/
│ ├── web/ # Frontend (Next.js) — chunking, OPFS, upload logic
│ └── server/ # Backend API (Hono) — bucket upload, DB ack
├── packages/
│ ├── ui/ # Shared shadcn/ui components and styles
│ ├── db/ # Drizzle ORM schema & queries
│ ├── env/ # Type-safe environment config
│ └── config/ # Shared TypeScript config
npm run dev— Start all apps in development modenpm run build— Build all appsnpm run dev:web— Start only the web appnpm run dev:server— Start only the servernpm run check-types— TypeScript type checkingnpm run db:push— Push schema changes to databasenpm run db:generate— Generate database client/typesnpm run db:migrate— Run database migrationsnpm run db:studio— Open database studio UI