Quickstart
Get a real export running in under 10 minutes: register a dataset, create a destination, run an export, and get back a manifest with a verifiable checksum.
1. Install
npm install @dataegress/sdk
2. Define a dataset
A dataset is a fetchPage function — your own query, with your own tenant
isolation. DataEgress never connects to your database directly (see
architecture.md).
// app/api/dataegress/transactions/route.ts (Next.js App Router example)
import { defineDataset } from "@dataegress/sdk";
import { db } from "@/lib/db";
const transactionsDataset = defineDataset({
id: "transactions",
fetchPage: async ({ tenantId, cursor, limit }) => {
const rows = await db.transaction.findMany({
where: { tenantId, id: { gt: cursor ? Number(cursor) : 0 } },
orderBy: { id: "asc" },
take: limit,
});
const nextCursor = rows.length < limit ? null : String(rows[rows.length - 1].id);
return { rows, nextCursor };
},
});
export const POST = transactionsDataset.handler();
Deploy this route. Its URL (e.g. https://your-app.com/api/dataegress/transactions)
is what you register as the dataset's fetchUrl.
3. Register the dataset with DataEgress
curl -X POST http://localhost:3001/api/datasets \
-H 'content-type: application/json' \
-d '{"id": "transactions", "fetchUrl": "https://your-app.com/api/dataegress/transactions"}'
4. Create a destination
curl -X POST http://localhost:3001/api/destinations \
-H 'content-type: application/json' \
-d '{
"tenantId": "acme",
"type": "s3",
"s3": {
"bucket": "acme-data-lake",
"region": "us-east-1",
"accessKeyId": "...",
"secretAccessKey": "..."
}
}'
Use "type": "signed_url" instead if you just want a downloadable link and
don't have a bucket yet — no s3 object needed.
5. Run an export
curl -X POST http://localhost:3001/api/exports \
-H 'content-type: application/json' \
-d '{"tenantId": "acme", "dataset": "transactions", "destination": "acme-s3", "format": "parquet"}'
# => { "id": "exp_...", "status": "pending", ... }
Poll it:
curl http://localhost:3001/api/exports/exp_...
When status is "completed", the response includes rowCount, checksum,
downloadUrl, and a full manifest (see manifest shape).
6. Schedule it
curl -X POST http://localhost:3001/api/schedules \
-H 'content-type: application/json' \
-d '{
"tenantId": "acme",
"dataset": "transactions",
"destination": "acme-s3",
"format": "parquet",
"cron": "0 2 * * *"
}'
Cron is a standard 5-field expression, evaluated in UTC. DataEgress checks for due schedules every minute and runs them through the exact same path as a manual export — no separate "scheduled execution" logic to keep in sync.
Demo: failure + retry
The seeded demo dataset (events, tenant acme) has two chaos tenants for
exercising the failure path without touching real infrastructure:
acme-flaky— fails once mid-export, then self-heals on Inngest's automatic retry. No user action needed.acme-stuck— fails on every automatic retry until you callPOST /api/exports/:id/retry(or click Retry in the dashboard), which unlocks it. Demonstrates the manual-retry path specifically.
Run either from the dashboard home page (/) or via:
curl -X POST http://localhost:3001/api/exports \
-H 'content-type: application/json' \
-d '{"tenantId": "acme-stuck", "dataset": "events", "destination": "demo-download", "format": "csv"}'
Where to go next
- docs/s3.md — S3/R2 destination setup and env vars for testing against a real bucket
- docs/architecture.md — why DataEgress never touches your database
- docs/limitations.md — what V1 deliberately does not do
- docs/use-cases/ — worked examples for specific problems