Skip to main content
Confect requires a small number of fixed entry-point files in your confect/ directory (see Project Structure). Beyond those, organize your API as colocated *.spec.ts/*.impl.ts pairs, one pair per group. The group’s name is the file’s path within confect/ (its stem for top-level groups, the dot-joined directory path for nested groups).

Spec and impl files

Name each group’s spec and impl with .spec.ts and .impl.ts suffixes. Each file must default-export its GroupSpec or GroupImpl; additional named exports on .spec.ts (for example error classes) are fine. A complete pair looks like this. The impl default-imports its sibling spec, passes it to FunctionImpl.make and GroupImpl.make, and finalizes the resulting layer with GroupImpl.finalize:
confect/notes.spec.ts
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);
confect/notes.impl.ts
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,
);
Run confect codegen after adding or changing specs and impls.
A file’s path within confect/ determines the public Convex API path it produces—confect/notes_and_random/notes.spec.ts becomes internal.notes_and_random.notes.list. Renaming a file or directory renames the API path, so treat these paths as part of your API’s contract.
Some test runners (such as Vitest and Jest) treat .spec.ts files as test files by default. If your test runner is picking up Confect spec files, configure it to exclude them. For example, in Vitest you can add "**/*.spec.ts" to the test.exclude array in your config.

Native Convex functions

When a group wraps native Convex functions (for use with components or other libraries), place the plain function definitions in a file named after the group—without a suffix. This puts all three files for a group side by side:
confect/
  workpool.ts          ← native Convex function definitions
  workpool.spec.ts     ← spec (type-only imports from workpool.ts)
  workpool.impl.ts     ← impl (runtime imports from workpool.ts)

Node actions

Node action groups follow the same .spec.ts/.impl.ts naming and nesting conventions as any other group. A group is Node-runtime when its spec is built with GroupSpec.makeNode() (rather than GroupSpec.make()); its impl is otherwise identical to a non-node impl, passing the database schema from _generated/schema. Confect emits Convex’s "use node" directive into the generated module based on the spec.
confect/
  email.spec.ts
  email.impl.ts

Nested groups

When a group contains subgroups, place each subgroup’s .spec.ts/.impl.ts pair inside a subdirectory named after the parent group. The parent group itself does not need a spec or impl file—it is composed from its subgroups by their paths.
confect/
  billing/
    invoices.spec.ts        ← group path billing.invoices
    invoices.impl.ts
    subscriptions.spec.ts   ← group path billing.subscriptions
    subscriptions.impl.ts

Full example

Putting it all together, a project using all of these conventions might look like this:
confect
_generated
convexSchema.ts
refs.ts
schema.ts
services.ts
spec.ts
registeredFunctions
env.ts
notes_and_random
notes.ts
random.ts
workpool.ts
email.ts
tables
notes.ts
users.ts
id.ts
notes_and_random
notes.spec.ts
notes.impl.ts
random.spec.ts
random.impl.ts
tables
notes.ts
users.ts
email.spec.ts
email.impl.ts
env.spec.ts
env.impl.ts
http.ts
workpool.ts
workpool.spec.ts
workpool.impl.ts