Skip to content

createActions

createActions<TUser, TServices>(config): object

Defined in: packages/actions/src/create-actions.ts:132

Creates a scoped action factory bound to a shared context provider.

Returns two functions — createAction and composeActions — that share the same getContext callback. This ensures every action in the application resolves its database, user, and grants consistently.

Services registered via the optional services config are injected into every action’s context as ctx.services, so handlers can access shared dependencies (HTTP clients, mailers, third-party APIs) without importing module-level singletons. Services are always defined on ctx.services — the factory fills in {} when no services are configured so handlers can destructure safely.

TUser = any

The shape of the authenticated user object.

TServices extends ActionServices = ActionServices

The shape of the registered services map.

ActionsConfig<TUser, TServices>

The ActionsConfig providing the getContext callback and optional services bag.

An object with createAction and composeActions functions.

composeActions: <TActions>(actions) => ComposedActions<TActions>

Combines multiple action definitions into a single route handler that dispatches by the _action discriminator field.

The returned ComposedActions object provides a unified .action handler, a .loader() wrapper that checks permissions for all actions at once, and a .client descriptor covering every action name.

TActions extends Record<string, ActionDefinition<any, any, any>>

A record mapping action names to their definitions.

TActions

An object of named action definitions to compose.

ComposedActions<TActions>

A ComposedActions object with combined handler, loader, and client descriptor.

const composed = composeActions({
deletePost,
publishPost,
unpublishPost,
});
export const action = composed.action;
export const loader = composed.loader(async ({ request, params }) => {
return { post: await getPost(params.slug) };
});

createAction: <TInput, TResult>(config) => ActionDefinition<TInput, TResult, TUser>

Defines a single permission-aware action.

Takes an OperationsFn that builds a database Operation from the parsed input and action context. Returns an ActionDefinition with .action, .loader(), .client, and .buildOperation facets.

TInput

The expected input shape for this action.

TResult

The return type of the action handler.

OperationsFn<TInput, TResult, TUser> |

{ handler: OperationsFn<TInput, TResult, TUser>; input?: InputParser<TInput>; }

OperationsFn<TInput, TResult, TUser>

The operation builder. Same shape as the legacy positional form.

InputParser<TInput>

Optional typed input parser, e.g. produced by defineInput({ ... }).

When set, the parser runs over the request’s raw input before the handler is invoked. Validation failures throw InvalidInputError with field-keyed messages — the call this fix exists for is Number(formData.get("position")) silently producing NaN, which z.number() / z.integer() rejects upfront.

ActionDefinition<TInput, TResult, TUser>

An ActionDefinition with action handler, loader wrapper, client descriptor, and build method.

const deletePost = createAction<{ postId: string }, Response>(
(db, input, ctx) =>
compose(
[db.delete(posts).where(eq(posts.id, input.postId))],
async (runDelete) => {
await runDelete({});
return redirect("/");
},
),
);
export const action = deletePost.action;
import { createActions } from "@cfast/actions";
export const { createAction, composeActions } = createActions({
getContext: async ({ request }) => {
const ctx = await requireAuthContext(request);
const db = createCfDb(env.DB, ctx);
return { db, user: ctx.user, grants: ctx.grants };
},
});
type Services = { nutritionApi: NutritionApi };
export const { createAction } = createActions<AppUser, Services>({
getContext: async ({ request }) => { ... },
services: { nutritionApi: createNutritionApi(env.NUTRITION_API_KEY) },
});
const lookupFood = createAction((db, input, ctx) => ({
permissions: [],
run: async () => ctx.services.nutritionApi.lookup(input.name),
}));