Backend and API Agents
By the Syncfusion DevTutorials team · Last updated
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:
- Read
api/openapi.json, the API specification. There is no export endpoint yet. - Add
GET /tickets/{id}/exportto the specification. Thestatusfield must be one of four allowed values, androwsmust be a whole number. - Create
api/export.js. - Run the contract check to verify that actual responses match the specification.
- 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:
- How to design an endpoint and its response schema.
- What an API specification is and why the contract should be defined first.
- How a contract check compares actual responses with the specification.
- How service code accesses data and what can make it slow.
- 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.
Contract first: the specification is written before the code. The frontend agent can start building against the contract while the backend agent writes the implementation.
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.
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:
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.
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:
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.
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:
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.
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:
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.
Backend Agent: GET /tickets/:id/export
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.
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:
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.