Plug in Supabase, Postgres, or webhooks to persist form responses.
By default, FieldCraft calls your onSubmit callback with a FormResponse and leaves storage up to you. Storage adapters add automatic persistence to a database or external service.
Available Adapters
| Package | Storage | Encryption | Drafts |
|---|
@squaredr/fieldcraft-supabase | Supabase tables | Field-level AES-256-GCM | Yes |
@squaredr/fieldcraft-postgres | PostgreSQL + Drizzle ORM | Payload AES-256-GCM | Yes |
@squaredr/fieldcraft-webhook | HTTP POST | HMAC-SHA256 signing | No |
All three adapters are free and open source (MIT licensed).
Install
# Pick one (or more)
npm install @squaredr/fieldcraft-supabase
npm install @squaredr/fieldcraft-postgres
npm install @squaredr/fieldcraft-webhook
How Adapters Work
Every adapter implements the SubmitAdapter interface. When the engine submits, it runs each adapter in parallel and collects results.
interface SubmitAdapter {
name: string;
submit(response: FormResponse): Promise<void>;
onError?: (error: Error) => void;
}
FormResponse
The data object passed to every adapter on submit:
type FormResponse = {
schemaId: string;
schemaVersion: string;
submittedAt: string; // ISO 8601
sessionToken: string;
values: Record<string, unknown>;
scores?: Record<string, number>;
totalScore?: number;
metadata?: Record<string, unknown>;
completionTimeMs?: number;
};
SubmitResult
Returned by engine.submit() with per-adapter status:
type SubmitResult = {
success: boolean;
adapterResults: {
adapterName: string;
success: boolean;
error?: string;
}[];
};
Usage with FormEngineRenderer
import { FormEngineRenderer } from "@squaredr/fieldcraft-react";
import { createSupabaseAdapter } from "@squaredr/fieldcraft-supabase";
const adapter = createSupabaseAdapter({ /* config */ });
<FormEngineRenderer
schema={schema}
adapters={adapter}
onSubmit={(response) => console.log("Done:", response)}
/>
Usage with createEngine
import { createEngine } from "@squaredr/fieldcraft-core";
import { createPostgresAdapter } from "@squaredr/fieldcraft-postgres";
import { createWebhookAdapter } from "@squaredr/fieldcraft-webhook";
const engine = createEngine(schema, {
adapters: [
createPostgresAdapter({ connectionString: "..." }),
createWebhookAdapter({ url: "https://...", secret: "..." }),
],
});
const result = await engine.submit();
// result.adapterResults[0] → postgres result
// result.adapterResults[1] → webhook result
Multiple Adapters
Pass an array to adapters to write to multiple destinations. All adapters run in parallel. The submission succeeds only if all adapters succeed.
Draft Adapters
The Supabase and Postgres packages also export draft adapters for server-side draft persistence:
import { createSupabaseAdapter } from "@squaredr/fieldcraft-supabase";
const adapter = createSupabaseAdapter({ /* config */ });
const engine = createEngine(schema, {
adapters: adapter,
draftAdapter: adapter.draftAdapter, // Also handles drafts
sessionToken: "user-123",
});
Building a Custom Adapter
import type { SubmitAdapter, FormResponse } from "@squaredr/fieldcraft-core";
const myAdapter: SubmitAdapter = {
name: "my-api",
async submit(response: FormResponse) {
const res = await fetch("https://api.example.com/forms", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(response),
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
},
onError(error) {
console.error("Adapter failed:", error);
},
};