Next.js template

Asynchronous file processing, ready to run

A working Next.js example that stores CSV uploads, sends processing jobs to RocketStack Queue, and runs the work asynchronously in a Function.

Object StorageQueueFunction

Architecture

Next.js routeObject StorageQueueFunction

Source path: examples/nextjs-file-processing

What the template includes

  • Next.js upload route

    App Router handler at POST /api/uploads with multipart validation.

  • File validation

    Server-side CSV type, size, and path-sanitization checks.

  • RocketStack Object Storage integration

    Stores the original CSV before queueing background work.

  • RocketStack Queue integration

    Enqueues concise job metadata — never the file contents.

  • CSV-processing Function

    Downloads, parses, and summarizes the stored CSV asynchronously.

  • Sample CSV and setup guide

    Bundled fictional customers.csv plus README and console inspection steps.

Architecture

Browser
  |
  v
Next.js upload route
  |
  v
RocketStack Object Storage
  |
  v
RocketStack Queue
  |
  v
RocketStack Function
  |
  v
Processing result

Interactive sample preview

Sample CSV

customer_idcompanyplansignup_date
CUST-1001Northwind Analyticsstarter2025-01-14
CUST-1002Blue Harbor Labsgrowth2025-02-03
CUST-1003Summit Field Costarter2025-02-18
CUST-1004Orbit Metricsgrowth2025-03-02
CUST-1005Harborline Systemsenterprise2025-03-21
CUST-1006Clearpath Studiostarter2025-04-05

Example result

Illustrative summary after Function processing.

Rows
12
Columns
4
Invalid rows
0

Key code excerpts

Validate and upload the file

Validate and upload the file
export async function POST(request: Request) {
  const formData = await request.formData()
  const entry = formData.get('file')
  if (!(entry instanceof File)) {
    return Response.json({ error: 'Missing file field' }, { status: 400 })
  }

  const validation = validateUploadFile(entry)
  if (!validation.ok) {
    const status = validation.code === 'too_large' ? 413 : 400
    return Response.json({ error: validation.message }, { status })
  }

  const jobId = createJobId()
  const objectPath = buildObjectPath(jobId, validation.fileName, getStoragePrefix())

  await uploadObject(entry, objectPath)
  await enqueueFileProcessingJob({
    jobId,
    type: 'csv.import',
    objectPath,
    originalFileName: validation.fileName,
    contentType: validation.contentType
  })

  return Response.json({ jobId, status: 'queued', fileName: validation.fileName }, { status: 202 })
}

Add the processing job to Queue

Add the processing job to Queue
export async function enqueueFileProcessingJob(
  payload: FileProcessingJob
): Promise<void> {
  const queueId = requiredEnv('ROCKETSTACK_QUEUE_ID')
  const queues = getQueuesClient()

  const response = await queues.enqueueMessage(queueId, {
    payload,
    idempotencyKey: payload.jobId
  })

  if (!response.data?.data?.messageId) {
    throw new Error('Queue enqueue returned an unexpected response')
  }
}

Process the stored CSV

Process the stored CSV
exports.handler = async function handler(event) {
  const payload = event?.payload
  if (!payload?.jobId || !payload?.objectPath || payload?.type !== 'csv.import') {
    throw new Error('Invalid queue delivery payload')
  }

  const summaryPath = resultPath(payload.jobId)
  if (await objectExists(summaryPath)) {
    return { success: true, jobId: payload.jobId, skipped: true }
  }

  await updateJobStatus(payload.jobId, 'processing')
  const csvText = await fetchObjectText(payload.objectPath)
  const parsed = parseCsv(csvText)

  const summary = {
    success: true,
    jobId: payload.jobId,
    objectPath: payload.objectPath,
    rowCount: parsed.rowCount,
    columnCount: parsed.columnCount,
    columns: parsed.columns,
    invalidRowCount: parsed.invalidRowCount,
    processedAt: new Date().toISOString()
  }

  await uploadTextObject(summaryPath, JSON.stringify(summary), 'application/json')
  await updateJobStatus(payload.jobId, 'completed', { summary })
  return summary
}

Setup steps

Open the README
  1. Step 1

    Clone the example

    Copy examples/nextjs-file-processing and install dependencies.

  2. Step 2

    Add environment variables

    Set ROCKETSTACK_API_KEY and ROCKETSTACK_QUEUE_ID in .env.local.

  3. Step 3

    Create Storage, Queue, and Function

    Deploy process-csv and point the Queue delivery target at it.

  4. Step 4

    Upload the sample CSV

    Use the bundled customers.csv or another safe CSV to exercise the pipeline.

Full install, upload, and troubleshooting steps are in examples/nextjs-file-processing/README.md.

Retry and duplicate-processing guidance

  • Deterministic result path

    The Function writes results/{jobId}/summary.json and skips work when that object already exists.

  • KV job status

    Job status is stored at file-job:{jobId} in RocketStack KV. Updates are best-effort and not atomic exactly-once claims.

  • Application database

    For irreversible side effects, persist completed job IDs in your own database before applying business logic.

Production considerations

  • Keep RocketStack API keys server-side only
  • Validate file type and size before upload
  • Sanitize object paths and reject traversal patterns
  • Queue only job metadata — never file contents
  • Limit parser memory usage with row and column caps
  • Monitor failed deliveries and dead-lettered messages in the console
  • Review Free plan usage limits before going live

Object Storage documentation · Queue documentation · Functions documentation · Pricing

Run the template with RocketStack

Create a free account, get an API key, and connect Object Storage, Queue, and Function resources used by the example.