Skip to main content
Confect functions are defined as a spec and impl pair. The spec declares each function’s name, arguments, and return type. The impl provides the handler logic.

Function types

ConstructorDescription
FunctionSpec.publicQueryPublic query function
FunctionSpec.publicMutationPublic mutation function
FunctionSpec.publicActionPublic action function
FunctionSpec.internalQueryInternal query function
FunctionSpec.internalMutationInternal mutation function
FunctionSpec.internalActionInternal action function
For Node.js actions, see Node Actions. To integrate plain Convex functions (for use with Convex components or other libraries), see Plain Convex Functions.

Defining a spec

Each function spec defines the function’s name, arguments schema, and returns schema. Function specs are grouped into group specs, which are combined into your API’s top-level spec. See The Spec/Impl Model for a full walkthrough.
confect/notes.spec.ts
import { GroupSpec, FunctionSpec } from "@confect/core";
import { Schema } from "effect";

import { Notes } from "./tables/Notes";

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

export const notes = GroupSpec.make("notes").addFunction(list);
confect/spec.ts
import { Spec } from "@confect/core";

import { notes } from "./notes.spec";

export default Spec.make().add(notes);

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 (like DatabaseReader, DatabaseWriter, Auth, etc.) inside your handler to interact with Convex.
confect/notes.impl.ts
import { FunctionImpl, GroupImpl } from "@confect/server";
import { Effect, Layer } from "effect";

import api from "./_generated/api";
import { DatabaseReader } from "./_generated/services";

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

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

export const notes = GroupImpl.make(api, "notes").pipe(
  Layer.provide(list),
);
Your impl must be the default export of an impl.ts file in your confect/ directory, and must be finalized with Impl.finalize.
confect/impl.ts
import { Impl } from "@confect/server";
import { Layer } from "effect";

import api from "./_generated/api";
import { notes } from "./notes.impl";

export default Impl.make(api).pipe(
  Layer.provide(notes),
  Impl.finalize,
);