Plugins
Add endpoints, hooks, error codes, field types, and matching client methods.
A plugin can add HTTP endpoints, lifecycle hooks, error codes, field types, and matching methods on createFormClient.
1. Creating a Server Plugin
Use definePlugin() and createFormEndpoint() from @dimah-form/server to build a server extension:
import {
createFormEndpoint,
defineErrorCodes,
definePlugin,
} from "@dimah-form/server";
import * as z from "zod";
// 1. Declare domain-specific error codes
export const ANALYTICS_ERROR_CODES = defineErrorCodes({
STATS_UNAVAILABLE: "Analytics data could not be computed",
});
// 2. Define the server plugin
export const analyticsPlugin = () =>
definePlugin({
id: "analytics",
$ERROR_CODES: ANALYTICS_ERROR_CODES,
// Add custom HTTP endpoints to the dimahForm router
endpoints: {
getFormStats: createFormEndpoint(
"/analytics/stats",
{
method: "GET",
query: z.object({
formId: z.string(),
}),
},
async ({ query, store, request }) => {
const list = await store.listResponses({
formId: query.formId,
limit: 100,
});
const total = list.responses.length;
const submitted = list.responses.filter(
(r) => r.status === "submitted",
).length;
return {
formId: query.formId,
totalResponses: total,
completionRate: total > 0 ? (submitted / total) * 100 : 0,
};
},
),
},
// Optional lifecycle hooks contributed by this plugin
hooks: {
afterSubmit: async ({ response }) => {
console.log(`[Analytics] Response ${response.id} submitted.`);
},
},
});2. Registering on the Server
Pass the plugin to dimahForm({ plugins: [...] }):
import { dimahForm, memoryAdapter } from "@dimah-form/server";
import { analyticsPlugin } from "@/lib/plugins/analytics-plugin";
import { forms } from "@/lib/forms";
export const form = dimahForm({
database: memoryAdapter(),
forms,
plugins: [analyticsPlugin()],
});
export type Form = typeof form;You can now call the plugin endpoint directly through form.api:
const stats = await form.api.getFormStats({
query: { formId: "feedback" },
});3. Creating a Companion Client Plugin
To expose typed methods on createFormClient, create a companion client plugin using defineClientPlugin():
import { defineClientPlugin } from "@dimah-form/react";
import { ANALYTICS_ERROR_CODES } from "./analytics-plugin";
export const analyticsClientPlugin = () =>
defineClientPlugin({
id: "analytics",
$ERROR_CODES: ANALYTICS_ERROR_CODES,
endpoints: ({ $fetch }) => ({
getStats: (formId: string) =>
$fetch<{
formId: string;
totalResponses: number;
completionRate: number;
}>(`/analytics/stats?formId=${encodeURIComponent(formId)}`, {
method: "GET",
}),
}),
});Register the client plugin with createFormClient:
"use client";
import { createFormClient } from "@dimah-form/react";
import type { Form } from "@/lib/form";
import { analyticsClientPlugin } from "@/lib/plugins/analytics-client";
export const formClient = createFormClient<Form>({
basePath: "/api/form",
plugins: [analyticsClientPlugin()],
});
// Call the plugin method from client components
const stats = await formClient.getStats("feedback");Plugin Options Reference
| Property | Type | Description |
|---|---|---|
id | string | Unique alphanumeric identifier for the plugin. |
$ERROR_CODES | ErrorCodeCatalog | Custom error codes declared via defineErrorCodes(). |
endpoints | Record<string, FormEndpoint> | Map of custom route handlers. |
hooks | DimahFormHooks | Pre-write and post-write lifecycle hooks. |
fieldTypes | FieldTypeDefinition[] | Custom field validators added by this plugin. |
dependsOn | string[] | Array of required plugin IDs that must be loaded before this plugin. |