> ## Documentation Index
> Fetch the complete documentation index at: https://confect.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Node Actions

> Define actions that run in the Node.js runtime.

Node actions are like regular actions, but they are executed in the Node.js runtime, so their handlers can use Node's built-in modules and npm packages that depend on them. You declare a node action group by building its spec with `GroupSpec.makeNode()` (rather than `GroupSpec.make()`); that marks the group as Node-runtime, and Confect adds Convex's [`"use node"`](https://docs.convex.dev/functions/runtimes#nodejs-runtime) directive to the generated module accordingly. A node action group is otherwise an ordinary group — the same colocated `.spec.ts`/`.impl.ts` files, following the same [naming and nesting conventions](/concepts/file-naming-conventions).

## Node Spec

A node spec uses `GroupSpec.makeNode()` and one of `FunctionSpec.publicNodeAction()` or `FunctionSpec.internalNodeAction()` to define functions.

```ts confect/email.spec.ts theme={null}
import { FunctionSpec, GroupSpec } from "@confect/core";
import * as Schema from "effect/Schema";

export default GroupSpec.makeNode().addFunction(
  FunctionSpec.publicNodeAction({
    name: "send",
    args: () =>
      Schema.Struct({
        to: Schema.String,
        subject: Schema.String,
        body: Schema.String,
      }),
    returns: () => Schema.Null,
  }),
);
```

## Node Impl

A node impl default-imports its sibling spec and passes the database schema from `_generated/schema` to `FunctionImpl.make` and `GroupImpl.make` — exactly like a non-node impl; only the spec differs (`GroupSpec.makeNode()`). Each handler runs in the Node.js runtime, so it has access to [`NodeContext` services](https://effect-ts.github.io/effect/platform-node/NodeContext.ts.html#nodecontext-type-alias) from the `@effect/platform` package.

```ts confect/email.impl.ts theme={null}
import { FunctionImpl, GroupImpl } from "@confect/server";
import { Command } from "@effect/platform";
import * as Console from "effect/Console";
import * as Duration from "effect/Duration";
import * as Effect from "effect/Effect";
import * as Layer from "effect/Layer";
import databaseSchema from "./_generated/schema";
import email from "./email.spec";

const send = FunctionImpl.make(
  databaseSchema,
  email,
  "send",
  Effect.fn(function* ({ to, subject, body }) {
    const result = yield* Command.make(
      "echo",
      `Sending email to ${to} with subject ${subject} and body ${body}…`,
    ).pipe(Command.stdout("pipe"), Command.string, Effect.orDie);

    yield* Console.log(result);
    yield* Effect.sleep(Duration.seconds(1));
    yield* Console.log("Email sent!");

    return null;
  }),
);

export default GroupImpl.make(databaseSchema, email).pipe(
  Layer.provide(send),
  GroupImpl.finalize,
);
```

## Calling Node Actions

Node actions are referenced like any other function. A node group at `confect/email.spec.ts` is reached at `refs.public.email`.

```ts theme={null}
import { useAction } from "@confect/react";
import refs from "../confect/_generated/refs";

const sendEmail = useAction(refs.public.email.send);

sendEmail({
  to: "user@example.com",
  subject: "Hello",
  body: "Hello from Confect!",
});
```
