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

# Operation Interfaces

> Share one operation shape across object types and discover implementations

Some business verbs apply to many kinds of objects: archiving an order and
archiving an invoice mean the same thing to the person clicking the button, even
though each needs its own code. An operation interface declares that shared
shape once. Each object type then supplies its own concrete operation that
implements it.

```ts bijection/archive.ts theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
import { defineOperation, defineOperationInterface } from "bijection/server";
import { v } from "bijection/values";
import { orders, invoices } from "./schema";

export const archive = defineOperationInterface({
  name: "Archivable.archive",
  args: { reason: v.string() },
  returns: v.null(),
});

export const archiveOrder = defineOperation({
  on: orders,
  target: { argument: "target" },
  implements: [archive],
  args: { target: v.id("order_records"), reason: v.string() },
  returns: v.null(),
  prepare: async (ctx, { target, reason }) => {
    await ctx.db.patch(target, { archived: true, archive_reason: reason });
    return null;
  },
});

export const archiveInvoice = defineOperation({
  on: invoices,
  target: { argument: "target" },
  implements: [archive],
  args: { target: v.id("invoice_records"), reason: v.string() },
  returns: v.null(),
  prepare: async (ctx, { target, reason }) => {
    await ctx.db.patch(target, { archived: true, archive_reason: reason });
    return null;
  },
});
```

## Declaring an interface

`defineOperationInterface` takes three fields:

* `name`: the interface's name. It starts with a letter or `_`, may contain
  letters, digits, `_` and `.`, and is at most 128 characters long.
* `args`: the shared arguments, **without** the target.
* `returns`: the shared result.

An interface owns no tables, rules, credentials or handler. It describes a
shape, and implementing it grants nothing.

## Implementing an interface

A concrete operation opts in with `implements`. Each implementation must:

* declare `target: { argument: "target" }`, with a required `target` argument
  that is an ID of its own object type;
* declare exactly the interface's other arguments, and exactly its result.

`defineOperation` refuses an implementation whose shape does not match. An
operation can implement up to 16 interfaces.

When you deploy, at most one operation per interface and object type is admitted
in a component, private operations included. Two implementations for the same
pair are refused rather than one being picked.

<Note>
  Nothing is inferred. An interface does not make a view's field editable, and
  does not map one argument name to another. Each implementation spells out its
  own change.
</Note>

## Discovering implementations

The exported interface is an ordinary query. Call it with an optional `on`
filter, the name of an object type in your schema, and it returns the public
implementations in that component that the caller can currently read:

```ts src/archive.ts theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
import {
  operationFunctionReference,
  operationImplementationReference,
  operationInvocationArgs,
  operationPreviewArgs,
} from "bijection/server";
import { api } from "../bijection/_generated/api";

const choices = await client.query(api.archive.archive, { on: "orders" });
if (choices.length !== 1) throw new Error("Archiving is not available");
const operation = operationImplementationReference(choices[0]);
const args = { target: orderId, reason: "Duplicate order" };

const preview = await client.mutation(
  operationFunctionReference(operation, "preview"),
  operationPreviewArgs(operation, args),
);

// When the user confirms, keep this reference and key for recovery.
const requestKey = crypto.randomUUID();
const request = operationInvocationArgs(operation, requestKey, args);
const receipt = await client.mutation(
  operationFunctionReference(operation, "invoke"),
  request,
);
```

`operationImplementationReference` turns a discovered implementation into an
ordinary [operation reference](/operations/calling-operations#operation-references).
From there, previewing, invoking, recovering and following a request work as
for any operation.

An empty result means no implementation is available to this caller, not that
none exists. Discovery grants nothing either: previewing and invoking still need
the concrete operation's grants.

<Warning>
  Resolve the interface once, before the first submission. To retry or recover a
  request, reuse the same concrete reference and request key. Discovering again
  could select a different implementation, and it cannot redirect a request that
  was already accepted.
</Warning>
