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

# Functions

> Define and implement Convex queries, mutations, and actions with Confect.

Confect functions are defined as a [spec and impl](/v9/concepts/spec-impl-model) pair. The spec declares each function's name, arguments, and return type. The impl provides the handler logic.

## Function types

| Constructor                           | Description                                             |
| ------------------------------------- | ------------------------------------------------------- |
| `FunctionSpec.publicQuery`            | Public query function                                   |
| `FunctionSpec.publicPaginatedQuery`   | Public [paginated query](#paginated-queries) function   |
| `FunctionSpec.publicMutation`         | Public mutation function                                |
| `FunctionSpec.publicAction`           | Public action function                                  |
| `FunctionSpec.internalQuery`          | Internal query function                                 |
| `FunctionSpec.internalPaginatedQuery` | Internal [paginated query](#paginated-queries) function |
| `FunctionSpec.internalMutation`       | Internal mutation function                              |
| `FunctionSpec.internalAction`         | Internal action function                                |

For Node.js actions, see [Node Actions](/v9/server/node-actions). To integrate plain Convex functions (for use with Convex components or other libraries), see [Plain Convex Functions](/v9/server/plain-convex-functions).

## Defining a spec

Each function spec defines the function's name, arguments schema, and returns schema. The `args`, `returns`, and (optional) `error` schemas are passed as `() => Schema` callbacks, evaluated lazily the first time the function is invoked. Function specs are added to a `GroupSpec` and default-exported from a `*.spec.ts` file. See [The Spec/Impl Model](/v9/concepts/spec-impl-model) for a full walkthrough.

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

import notes from "./_generated/tables/notes";

const list = FunctionSpec.publicQuery({
  name: "list",
  args: () => Schema.Struct({}),
  returns: () => Schema.Array(notes.Doc),
});

export default GroupSpec.make().addFunction(list);
```

Run `confect codegen` after adding or changing specs.

### Typed errors

A spec can also declare an optional `error` schema. When it does, the corresponding handler's `Effect` error channel is typed as that schema, and the failure is decoded for callers at every call site (React hooks, JS clients, and tests). See [Error Handling](/v9/server/error-handling) for the full walkthrough.

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

import { Id } from "./_generated/id";
import notes from "./_generated/tables/notes";

class NoteNotFound extends Schema.TaggedError<NoteNotFound>()("NoteNotFound", {
  noteId: Id("notes"),
}) {}

FunctionSpec.publicQuery({
  name: "getOrFail",
  args: () => Schema.Struct({ noteId: Id("notes") }),
  returns: () => notes.Doc,
  error: () => NoteNotFound,
});
```

### Paginated queries

Define a paginated query with `FunctionSpec.publicPaginatedQuery` (or `internalPaginatedQuery`). Instead of `returns`, pass an `item` schema — the type of one element in a page. The optional `args` schema declares only your own arguments; Confect composes the Convex-facing schemas for you, adding the `paginationOpts` argument (with all of Convex's [pagination protocol fields](https://docs.convex.dev/database/pagination)) and wrapping `item` in a `PaginationResult` returns schema.

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

import notes from "./_generated/tables/notes";

FunctionSpec.publicPaginatedQuery({
  name: "listByAuthor",
  args: () => Schema.Struct({ author: Schema.String }), // optional; omit for no extra args
  item: () => notes.Doc,
});
```

Do not declare `paginationOpts` in `args` — it is added automatically, and declaring it yourself is a type (and runtime) error.

The handler receives the decoded args including `paginationOpts`, which it typically forwards directly to [`paginate`](/v9/server/database/reading#paginate):

```ts confect/notes.impl.ts theme={null}
FunctionImpl.make(
  databaseSchema,
  notes,
  "listByAuthor",
  ({ author, paginationOpts }) =>
    Effect.gen(function* () {
      const reader = yield* DatabaseReader;

      return yield* reader
        .table("notes")
        .index("by_creation_time", "desc")
        .paginate(paginationOpts, (q) => q.eq(q.field("author"), author));
    }).pipe(Effect.orDie),
);
```

Paginated queries support the optional `error` schema like any other function. On the client, consume them with [`usePaginatedQuery`](/v9/clients/react#usepaginatedquery).

## Implementing functions

Each function impl contains a handler that implements the function's logic. Function impls are composed into group impls using Effect layers.

The handler receives the decoded arguments as its first parameter and returns an `Effect`. Use the generated [services](/v9/concepts/services) (like `DatabaseReader`, `DatabaseWriter`, `Auth`, etc.) inside your handler to interact with Convex.

```ts confect/notes.impl.ts theme={null}
import { FunctionImpl, GroupImpl } from "@confect/server";
import * as Effect from "effect/Effect";
import * as Layer from "effect/Layer";

import databaseSchema from "./_generated/schema";
import { DatabaseReader } from "./_generated/services";
import notes from "./notes.spec";

const list = FunctionImpl.make(databaseSchema, notes, "list", () =>
  Effect.gen(function* () {
    const reader = yield* DatabaseReader;

    return yield* reader
      .table("notes")
      .index("by_creation_time", "desc")
      .collect();
  }).pipe(Effect.orDie),
);

export default GroupImpl.make(databaseSchema, notes).pipe(
  Layer.provide(list),
  GroupImpl.finalize,
);
```

`GroupImpl.finalize` is the per-group completeness check: it only typechecks once every function declared by the spec has been provided to the group layer.

<Note>
  Convex bundles a deployment into a single artifact, but a function's cold
  start only evaluates the module graph reachable from its entry point. Confect
  emits one Convex module per group, so cold-starting a function only evaluates
  its own group's spec, impl, and the tables it touches. To keep that cold start
  fast, import Effect from its submodule paths (`import * as Schema from
      "effect/Schema"`) rather than the `effect` barrel in your `confect/` files—a
  barrel import pulls the whole `Schema` namespace into the module graph your
  function evaluates at cold start, even when you use only a small part of it.
</Note>
