How to import CSV files in Serverless Stack (SST)

7 min read
Learn how to build a CSV import feature in Serverless Stack (SST). Step-by-step guide for developers integrating spreadsheet uploads in SaaS applications.

How to Import CSV Files into a Serverless Stack (SST) App Using CSVBox

If you’re building a serverless app with SST and need to upload and process CSV data—like customer lists, inventory updates, or transaction records—this guide shows how to implement a production-ready CSV import workflow using CSVBox, with pragmatic security and developer patterns in 2026.

This solution streamlines spreadsheet data handling in SST by combining:

  • A React-based CSV import UI via the CSVBox widget
  • A scalable backend that receives validated CSV data via AWS Lambda webhooks

Ideal for full-stack developers, SaaS teams, and engineers who want a secure, no-fuss bulk import experience—without building upload, parsing, and validation logic from scratch.


Why CSV import is tricky in serverless applications

Serverless frameworks like Serverless Stack (SST) are great for AWS-native apps, but handling file uploads (spreadsheets in particular) raises common issues:

  • Lambda functions are stateless and not designed for persistent file storage
  • Handling multipart/form-data and streaming large files is complex
  • Most solutions need object storage (S3) and parsing/validation pipelines
  • You must implement robust mapping, deduplication, and retry logic

CSVBox offloads the browser upload, column mapping, and validation steps, delivering structured JSON to your backend webhook so your Lambdas just consume clean data.

At a high level the flow is: file → map → validate → submit → webhook.


What you’ll build

Steps covered:

  1. Create a CSVBox widget and map your fields
  2. Create an SST API route (Lambda) to receive CSVBox webhooks
  3. Embed CSVBox in a React frontend and surface import feedback

By the end you’ll have a serverless pipeline that receives validated rows ready for persistence (DynamoDB, Postgres, etc.) and error handling.


Prerequisites

To follow this guide you’ll need:

  • An SST v2 app with a React/Next.js frontend
  • Basic knowledge of AWS Lambda, API Gateway, and environment secrets
  • A CSVBox account and a configured widget (see CSVBox dashboard)

Step 1 — Configure a CSVBox widget

In the CSVBox dashboard:

  1. Create a new Widget
  2. Upload a sample CSV and map columns to your desired fields (Name, Email, Phone, etc.)
  3. Configure validation rules and required fields so the widget can surface errors to end users
  4. For webhook URL, you can use a placeholder until your SST endpoint is deployed
  5. Save the widget and note:
    • license_key
    • client_uuid
    • (optional) webhook signing secret if you enable webhook verification

These identifiers are used to initialize the widget in the browser and to verify webhook requests server-side.


Step 2 — Add a webhook endpoint in SST

CSVBox delivers imports to your backend as JSON via webhook. Your Lambda should validate the webhook (optional signing), parse rows, and persist or queue them.

Create an SST project (if needed):

npx create-sst@latest csv-import-sst
cd csv-import-sst

Define the API route in stacks/MyStack.ts:

import { StackContext, Api, Secrets } from "sst/constructs";

export function MyStack({ stack }: StackContext) {
  const api = new Api(stack, "Api", {
    routes: {
      "POST /import-webhook": "packages/functions/src/importWebhook.main",
    },
  });

  // Store the webhook secret (set via SST secrets or environment)
  // stack.addSecrets({ CSVBOX_WEBHOOK_SECRET: "..." });

  stack.addOutputs({
    ApiEndpoint: api.url,
  });
}

Note: api.url is the base URL returned by SST. Use the full URL you get after deploy (api.url + “import-webhook”) when configuring the widget webhook.

Lambda handler with optional signature verification (recommended)

  • Store your CSVBox webhook secret in SST secrets or environment variables.
  • Verify incoming webhooks using a timing-safe HMAC check when a secret is configured.

Example importWebhook handler (Node.js / TypeScript):

import { APIGatewayProxyHandler } from "aws-lambda";
import crypto from "crypto";

export const main: APIGatewayProxyHandler = async (event) => {
  try {
    const payload = event.body || "";
    // Header name may vary; check CSVBox docs or your widget settings
    const signatureHeader = event.headers["x-csvbox-signature"] || event.headers["X-Csvbox-Signature"];
    const secret = process.env.CSVBOX_WEBHOOK_SECRET;

    if (secret && signatureHeader) {
      const expected = crypto.createHmac("sha256", secret).update(payload).digest("hex");
      // Use timingSafeEqual to mitigate timing attacks
      const ok = crypto.timingSafeEqual(Buffer.from(expected, "hex"), Buffer.from(signatureHeader, "hex"));
      if (!ok) {
        console.warn("Invalid webhook signature");
        return { statusCode: 401, body: JSON.stringify({ error: "Invalid signature" }) };
      }
    }

    const body = JSON.parse(payload);
    // CSVBox webhook typically includes: data (rows), meta, summary
    const rows = body.data || [];
    console.log("Received rows:", rows.length);

    // TODO: validate shape, deduplicate, persist to DB or queue for processing
    // Example: insert rows into DynamoDB or push to an SQS queue

    return { statusCode: 200, body: JSON.stringify({ status: "ok" }) };
  } catch (err) {
    console.error("Error processing webhook:", err);
    return { statusCode: 400, body: JSON.stringify({ error: "Invalid payload" }) };
  }
};

Deploy your stack:

npx sst deploy

When deployment finishes, copy the full webhook URL (ApiEndpoint + route) and paste it into the CSVBox widget webhook field.

Example: https://abcd1234.execute-api.us-east-1.amazonaws.com/import-webhook


Step 3 — Embed the CSVBox widget in your React frontend

The CSVBox React component renders the upload UI (iframe) and manages mapping/validation in the browser. Your frontend only needs to initialize the widget and optionally handle client-side events.

Install the package:

npm install --save csvbox-react

Create components/CSVUploader.tsx and surface environment variables safely (Next.js requires NEXT_PUBLIC_ prefix for client-side vars):

import React from "react";
import { CSVBox } from "csvbox-react";

const CSVUploader = () => {
  return (
    <div>
      <h3>Import CSV</h3>
      <CSVBox
        licenseKey={process.env.NEXT_PUBLIC_CSVBOX_LICENSE_KEY}
        clientUuid={process.env.NEXT_PUBLIC_CSVBOX_CLIENT_UUID}
        user={{
          user_id: "123",
          name: "Test User",
          email: "test@example.com",
        }}
        onImport={(summary) => {
          // summary typically contains counts and status
          console.log("Import summary:", summary);
          // Optionally show a success banner or trigger a refresh
        }}
      />
    </div>
  );
};

export default CSVUploader;

Add the component to a page (pages/index.tsx):

import CSVUploader from "../components/CSVUploader";

export default function Home() {
  return (
    <div>
      <h1>CSV Import Page</h1>
      <CSVUploader />
    </div>
  );
}

Run the frontend:

npm run dev

The widget will handle the file selection, mapping, and validation UI. Once a user completes an import, CSVBox posts the processed rows to your webhook server-to-server.


Webhook payload shape (what to expect)

CSVBox webhooks typically deliver a JSON payload containing:

  • data: array of mapped rows (objects)
  • meta: widget and upload metadata (mapping, file name, timestamps)
  • summary: counts (processed, errors, skipped) and validation results

Design your Lambda to:

  • Validate required fields and types
  • Deduplicate rows (by configured unique key)
  • Persist valid rows or enqueue for background processing
  • Return 2xx quickly to acknowledge receipt and avoid retries

Security and operational tips

  • Verify webhooks using the HMAC signing secret (store in SST secrets)
  • Use environment variables (NEXT_PUBLIC_* for client) for license/client identifiers
  • Keep heavy processing off the webhook path—persist and process asynchronously (SQS, Step Functions, background worker)
  • Monitor Lambda invocations and errors in CloudWatch and configure alerts
  • Restrict access to persistence layers (DynamoDB/Postgres) via least-privilege IAM roles

Example project layout

A practical SST + CSVBox project might look like:

csv-import-sst/
├── apps/
│   └── web/
│       └── pages/
│       └── components/
│           └── CSVUploader.tsx
├── packages/
│   └── functions/
│       └── src/
│           └── importWebhook.ts
├── stacks/
│   └── MyStack.ts

FAQs and troubleshooting

Why isn’t my webhook triggering?

  • Ensure the webhook URL is the full HTTPS ApiEndpoint + route returned by SST
  • Verify your webhook secret (if configured) matches the secret used in the widget
  • Check Lambda logs in CloudWatch or use the SST Console to view request traces
  • Enable Webhook Debug Mode in CSVBox if available

Why do I see duplicate or unexpected rows?

  • Confirm column mappings and unique identifier configuration in the widget
  • Inspect the webhook payload (data/meta/summary) to confirm row shape
  • Implement idempotency or deduplication in your backend

Why am I seeing CORS errors?

  • The CSVBox widget runs in an iframe and communicates directly with the CSVBox servers; CORS issues are typically not relevant for webhooks
  • Your frontend doesn’t need to be the webhook receiver—the webhook is server-to-server

Why use CSVBox with SST (in 2026)

Pairing CSVBox with SST removes the heavy lifting of uploads and mapping:

  • User-friendly drag-and-drop import UI and mapping
  • Validation in the browser before submission
  • Clean JSON delivered to your webhook for server-side processing
  • Faster developer iteration: focus on data models, not parsers

This workflow speeds shipping bulk import features for SaaS products and internal tools.


Next steps

  • Add validation and persistence in importWebhook.ts (DynamoDB, Postgres)
  • Move secrets (CSVBOX_WEBHOOK_SECRET) into SST Secrets and reference them securely
  • Implement asynchronous processing for large imports (SQS + worker)
  • Create CSVBox templates to standardize incoming exports and reduce mapping errors

Resources


With CSVBox and SST you can add reliable CSV import features to your app quickly—focus on business logic while keeping imports secure and auditable.

Related Posts