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

# The Spec/Impl Model

> Understand how Confect separates function interfaces from their implementations.

Your Confect API is broken up into two parts: a **spec** and its implementation (or **impl**).

## Spec

A spec defines the interface of your Confect API. It is made up of group specs and function specs. Each function spec defines the function's name, arguments schema, and returns schema—but not the function's logic. The `args` and `returns` schemas (and an optional `error` schema) are passed as `() => Schema` callbacks, evaluated lazily on first invocation.

Specs are built using `@confect/core`, which means they can be shared between the server and client. This separation is what enables end-to-end schema decoding and encoding: the client knows the exact shape of every function's arguments and return value without importing any server code.

Each group spec is the default export of a `*.spec.ts` file. The group's name comes from the file's path—its stem for top-level groups, or the dot-joined directory path for nested groups:

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

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

export default GroupSpec.make().addFunction(
  FunctionSpec.publicQuery({
    name: "list",
    args: () => Schema.Struct({}),
    returns: () => Schema.Array(notes.Doc),
  }),
);
```

## Impl

An impl provides the logic for each function declared in your spec. Each impl is the default export of a `*.impl.ts` file colocated with its sibling spec. It default-imports that sibling spec, passes it to `FunctionImpl.make` and `GroupImpl.make`, and finalizes the resulting layer with `GroupImpl.finalize`:

```ts confect/notes_and_random/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` rejects, at compile time, any pipeline that has not provided a `FunctionImpl` for every function declared by the spec. This guarantees that the group is fully implemented before it is handed to the generated `convex/` module.

Run `confect codegen` after adding or changing specs and impls.
