# Backend and API Agents

Course: [AI Agents](https://www.devtutorials.ai/learn/ai-agents) · Section: Agents for Development Workflows
Source: https://www.devtutorials.ai/learn/ai-agents/dev-workflows/backend-agents

> How a backend agent writes the API contract first, then the code, and checks every response against the contract and the speed limit.

Think about a restaurant kitchen and its menu.

The menu is a promise. It says that if you order dish 12, you will receive pasta with tomato sauce and a side salad. The waiter takes orders using the menu, and the cook prepares the food using the same menu. Neither person needs to ask the other what dish 12 means.

If the cook quietly changes dish 12 to rice, the waiter still promises pasta and the customer gets something unexpected. If the cook prepares one strand of pasta at a time, the meal is correct but takes far too long.

A **backend agent** builds the kitchen part of an application. The menu is the **API contract**.

**The main idea:** A backend agent creates the contract first and then writes the code. It checks that every response follows the contract and that the code performs efficiently.

## A Real-World Example

The frontend agent's panel needs data: the export status and the row count. The planning agent's "contract" and "api" tasks are handled by the backend agent:

1. Read `api/openapi.json`, the API specification. There is no export endpoint yet.
2. Add `GET /tickets/{id}/export` to the specification. The `status` field must be one of four allowed values, and `rows` must be a whole number.
3. Create `api/export.js`.
4. Run the **contract check** to verify that actual responses match the specification.
5. Run the **speed check** to see how many database calls a request makes.

The first version of the code fails both checks. The second version passes both.

**In simple words:** Define the promise first, build the implementation second, then verify that the implementation matches the promise and performs efficiently.

## What You Will Learn

In this lesson, you will learn:

1. How to design an endpoint and its response schema.
2. What an API specification is and why the contract should be defined first.
3. How a contract check compares actual responses with the specification.
4. How service code accesses data and what can make it slow.
5. How a speed check counts database calls.

The backend agent has four tools: `read_file`, `write_file`, `check_contract`, and `check_speed`. It can only modify files in `api/`. The checks run the actual code in your browser against a small test database that counts every database call.

## API Design

### Endpoint Design

An **endpoint** is an address that the frontend uses to request data, such as `GET /tickets/4411/export`. A good endpoint has one clear purpose and is named after the resource it returns.

### Schema Design

A **schema** describes the structure of a response. It defines which fields exist, which data types they use, and which values are allowed.

```js runnable title="A response schema and two responses"
const schema = {
  required: ["status", "rows"],
  properties: { status: { enum: ["queued", "running", "done", "failed"] }, rows: { type: "integer" } },
};

function check(response) {
  const problems = [];
  for (const k of schema.required) if (!(k in response)) problems.push("missing " + k);
  for (const [k, v] of Object.entries(response)) {
    const p = schema.properties[k];
    if (!p) problems.push(k + " is not in the contract");
    else if (p.enum && !p.enum.includes(v)) problems.push(k + " " + JSON.stringify(v) + " is not allowed");
    else if (p.type === "integer" && !Number.isInteger(v)) problems.push(k + " must be a whole number");
  }
  return problems.length ? problems.join("; ") : "matches";
}

console.log(check({ status: "done", rows: 80000 }));
console.log(check({ status: "finished", count: 80000 }));
```

Expected output:

```text
matches
missing rows; status "finished" is not allowed; count is not in the contract
```

The second response looks reasonable to a person, but it breaks the contract in three different ways. The frontend expects a field called `rows`, so if that field is missing or named differently, the frontend will not display the value correctly.

### The API Spec

An **API spec** is the written contract for all endpoints. A common format is OpenAPI, and this project stores its specification in `api/openapi.json`.

**Contract first** means writing the specification *before* writing the code. This has several benefits:

- The frontend agent can start building at the same time using the specification.
- The contract check has a defined contract to validate against.
- Any change to the contract becomes a deliberate decision instead of an accidental code change.

```js runnable title="Does the spec have the endpoint yet?"
const spec = {
  paths: {
    "/tickets/{id}": { get: {} },
  },
};

function has(spec, method, path) {
  return Boolean(spec.paths[path] && spec.paths[path][method]);
}

console.log("GET /tickets/{id}: " + has(spec, "get", "/tickets/{id}"));
console.log("GET /tickets/{id}/export: " + has(spec, "get", "/tickets/{id}/export"));
spec.paths["/tickets/{id}/export"] = { get: { required: ["status", "rows"] } };
console.log("after adding it: " + has(spec, "get", "/tickets/{id}/export"));
```

Expected output:

```text
GET /tickets/{id}: true
GET /tickets/{id}/export: false
after adding it: true
```

The agent's first task is to perform this check. If the endpoint does not exist in the specification, the agent must update the contract before writing any code. This is why the contract comes first.

## Contract Checks

A **contract check** runs the actual code with sample inputs and compares every response against the API specification. In the Agent Lab, `check_contract` calls `getExport` for four sample tickets, one for each allowed status.

```js runnable title="Run the code, then check what it returns"
const statuses = { 1: "queued", 2: "running", 3: "done", 4: "failed" };
const db = { ticket: (id) => ({ status: statuses[id] }), count: (id) => (statuses[id] === "done" ? 80000 : 0) };

function getExportV1(id) { const t = db.ticket(id); return { status: t.status, count: db.count(id) }; }
function getExportV2(id) { const t = db.ticket(id); return { status: t.status, rows: db.count(id) }; }

for (const fn of [getExportV1, getExportV2]) {
  const bad = [1, 2, 3, 4].filter((id) => !("rows" in fn(id)));
  console.log(fn.name + ": " + (bad.length ? bad.length + " of 4 responses miss rows" : "4 of 4 match"));
}
```

Expected output:

```text
getExportV1: 4 of 4 responses miss rows
getExportV2: 4 of 4 match
```

One incorrect field name causes every response to fail the contract check. The check catches the problem immediately, before any frontend code uses the response.

## Backend Development

### Service Code

**Service code** contains the logic behind an endpoint. It gets the ticket, determines the status, counts the rows, and builds the response.

### Data Access

**Data access** is how the code reads information from the database. Every database call takes time, so reducing unnecessary calls is one of the most important ways to improve performance.

### Performance Checks

A **performance check** measures whether the code is efficient enough. The Agent Lab's `check_speed` tool does not use a stopwatch because timing can vary between computers. Instead, it counts how many database calls are made for a single request. The limit is 5 database calls.

```js runnable title="Count database calls: walk the rows, or ask for the count"
let calls = 0;
const db = {
  ticket: () => { calls++; return { status: "done" }; },
  row: (i) => { calls++; return i < 80000; },
  count: () => { calls++; return 80000; },
};

function slow() { db.ticket(); let n = 0; while (db.row(n)) n++; return n; }
function fast() { db.ticket(); return db.count(); }

calls = 0; const a = slow(); console.log("walk every row: " + a + " rows, " + calls + " database calls");
calls = 0; const b = fast(); console.log("ask for the count: " + b + " rows, " + calls + " database calls");
```

Expected output:

```text
walk every row: 80000 rows, 80002 database calls
ask for the count: 80000 rows, 2 database calls
```

Both versions return the correct result. However, one version makes 80,002 database calls, while the other makes only 2. The contract check cannot detect this difference because both responses follow the contract. That is why a speed check is needed as well.

Contract first: the agent reads the specification, reads the request, and adds the export endpoint to `api/openapi.json`. Its first implementation walks through every row and uses the field name `count`.

Both checks fail. The agent then fixes both issues in a single update, both checks pass, and the task is completed in 10 steps.

```js runnable title="A done rule that needs both checks"
function canFinish(results) {
  const failing = Object.entries(results).filter(([, r]) => !r.startsWith("PASS")).map(([name]) => name);
  return failing.length ? "not done: " + failing.join(", ") + " failing" : "done";
}

console.log(canFinish({ contract: "PASS 4 of 4", speed: "FAIL 80002 calls" }));
console.log(canFinish({ contract: "FAIL missing rows", speed: "FAIL 80002 calls" }));
console.log(canFinish({ contract: "PASS 4 of 4", speed: "PASS 2 calls" }));
```

Expected output:

```text
not done: speed failing
not done: contract, speed failing
done
```

**Easy Way to Remember:** **menu first, then cook, then taste and time it.** Contract, code, contract check, speed check.

Never change the contract just to make the contract check pass. If the contract needs to change, that decision must involve the planner and the frontend agent as well.

## Common Mistakes

| Mistake | Wrong Idea | Correct Idea | What Goes Wrong |
|---|---|---|---|
| Code first | "We'll write the spec later." | Write the contract first. | The frontend and backend end up using different response formats. |
| Field name mismatch | "`count` and `rows` mean the same thing." | Use the exact field names defined in the contract. | The frontend cannot find the expected data and shows nothing. |
| One database call per row | "It returns the correct result." | Ask the database for the count directly. | A single request can generate 80,002 database calls. |
| Stopwatch-based performance tests | "I'll measure it on my laptop." | Count database calls or use fixed limits. | Results vary from one machine to another. |
| Changing the contract to pass the check | "Just update the spec." | Contract changes should be a team decision. | Other agents may break without knowing the contract changed. |

## Exercises

Each exercise ends with a value on the last line. Press **Check** to compare it with the expected answer.

## What to Remember

- Menu first, then cook, then taste and time it.
- The API specification is the contract. Write the contract before writing the code.
- A contract check runs the actual code and verifies that every response matches the contract.
- Count database calls during performance checks. Reading rows one by one is a common cause of slow code.
- Finish only when both the contract check and the speed check pass.

Next: **Code Review Agents**, the agents that check a change and track down bugs.
