Define the API rules clearly first, then let AI write the code.

In AI-assisted development, interface design is an area that easily “looks like it works, but gets increasingly messy over time.”

Ask AI to write a few APIs for you, and it might quickly generate:

/getUser
/createUser
/update_user
/delete-user
/userList

They all work functionally. But as the project continues to grow, you easily end up with:

Same resource, different naming
Same action, different HTTP methods
Same error, different response structures
Same pagination, different field names

Eventually, frontend developers, backend developers, testers, and even AI itself start guessing:

“How exactly should this API be called?”

What’s truly missing here is not coding ability, but:

A unified API contract.


📌 Technical Profile

RESTful API (Representational State Transfer Application Programming Interface)

Is a common network interface design style.

It emphasizes designing URLs around “resources” and expressing operations through HTTP methods.

For example:

GET    /agents
POST   /agents
GET    /agents/12
PUT    /agents/12
DELETE /agents/12

Here:

  • GET: Read a resource
  • POST: Create a resource
  • PUT: Fully update a resource
  • PATCH: Partially modify a resource
  • DELETE: Delete a resource

And:

Contract First

Refers to:

Defining what the interface looks like first, then implementing the internal logic.

In other words, first determine:

What is the URL?
What is the HTTP method?
What are the input fields?
What are the output fields?
How are errors returned?
What are the status codes?

Then developers or AI implement the code according to this contract.


A Simple Analogy: An API Is Like a Restaurant Menu

You can think of a software interface as a restaurant’s menu.

The menu states:

Kung Pao Chicken
Price: 38 yuan
Spiciness: Medium
Portion: 1 serving

The customer doesn’t need to know:

Who the chef is
What brand the wok is
How the kitchen prep works
How much oil goes into the chicken

The customer only needs to know:

I order from the menu, and you serve me what’s on the menu.

APIs work the same way.

The frontend shouldn’t need to care about:

How the database is queried
How the Service is implemented
How the Repository is written

It only needs to know:

Request URL
Request method
Parameter format
Response result
Error format

So an API contract is like:

The menu a software system publishes to the outside world.

If the menu changed every day:

Yesterday it was "Kung Pao Chicken"
Today it's "Spicy Diced Chicken"
Tomorrow it becomes "Chicken Combo A"

Customers would be driven crazy — and the same goes for APIs.


1. The Core of RESTful: URLs Express “Resources,” HTTP Methods Express “Actions”

This is the most important concept to understand about RESTful.

Many projects start out writing things like this:

/getAgent
/createAgent
/updateAgent
/deleteAgent

This approach essentially:

Puts actions into the URL.

Whereas RESTful recommends:

GET    /agents
POST   /agents
PUT    /agents/{agent_id}
DELETE /agents/{agent_id}

Because:

/agents

Represents the resource:

The Agent collection.

As for “read, create, update, delete” — that’s expressed by HTTP methods.

So the interface language becomes very unified:

Resource + HTTP Method

Instead of:

Resource + Custom Verb + Custom Naming Convention

2. Why Is This Rule Especially Important for AI Programming?

Human developers working in a project for a few years will gradually form habits.

But AI is different. Every time AI generates code, it is essentially reasoning from scratch:

What should this API be called?
Should this update use POST or PUT?
What does a successful deletion return?
What are the pagination fields called?

Without architectural rules, AI might generate the following the first time:

POST /createAgent

Next time it generates:

POST /agents/create

And the time after that:

POST /agent

All three sets of APIs work, but the project has already started to lose control.

So:

RESTful isn’t about pursuing “textbook beauty” — it’s about reducing AI’s room for free improvisation.


3. API Design Rules

3.1 Unified Resource Naming

Resource names should use:

Nouns
Plural form
Consistent naming style

For example:

/users
/agents
/tools
/documents
/knowledge-bases

Rather than:

/userList
/getAgents
/toolManage
/queryDocument

miniagent’s current backend APIs use clear resource-oriented paths.

For example, the main program uniformly registers:

app.include_router(
    admin_agent_router,
    prefix="/api/v1/admin/agents",
    tags=["Admin - Agent"]
)

app.include_router(
    admin_tool_router,
    prefix="/api/v1/admin/tools",
    tags=["Admin - Tool"]
)

app.include_router(
    admin_document_router,
    prefix="/api/v1/admin/documents",
    tags=["Admin - Document"]
)

You can see the resource naming maintains:

/agents
/tools
/documents
/knowledge-bases
/prompts
/system-settings

Without stuffing actions like:

get
create
delete
update

into the URL.


3.2 HTTP Methods Express Actions

Take miniagent’s Agent management APIs as an example.

Read the list:

@router.get("")
async def list_agents(...):

Read a single Agent:

@router.get("/{agent_id}")
async def get_agent(...):

Create:

@router.post("")
async def create_agent(...):

Update:

@router.put("/{agent_id}")
async def update_agent(...):

Delete:

@router.delete("/{agent_id}")
async def delete_agent(...):

Combined together:

GET    /api/v1/admin/agents
GET    /api/v1/admin/agents/{agent_id}
POST   /api/v1/admin/agents
PUT    /api/v1/admin/agents/{agent_id}
DELETE /api/v1/admin/agents/{agent_id}

Even if you’ve never seen the miniagent codebase, you can basically guess what these APIs do.

This is what good API design brings:

Predictability.


3.3 Don’t Misuse PUT and PATCH

This is a place where AI gets very easily confused.

PUT typically means:

Update a resource.

For example:

PUT /agents/12

Means update the Agent with ID 12.

And PATCH means:

Partial Update

That is, only modifying part of the resource’s state.

miniagent has a very intuitive example:

@router.patch("/{agent_id}/toggle")
async def toggle_agent_active(...):

Here, it’s not resubmitting the entire Agent — it’s just:

Toggling whether the Agent is active.

So using PATCH is more semantically appropriate.

For beginners, you can start by remembering:

PUT   → Update a resource
PATCH → Partial modification

3.4 RESTful Doesn’t Mean “Actions Must Never Appear in URLs”

Many people take RESTful to the other extreme after learning it:

Absolutely no actions should ever appear in URLs.

That’s actually unnecessary.

Some business operations are not simple CRUD.

CRUD stands for:

Create, Read, Update, Delete

For example:

/agents/{id}/toggle

Expresses:

Toggle the Agent’s state.

Or in the future there might be:

/documents/{id}/reindex
/tasks/{id}/cancel
/agents/{id}/run

These are essentially business commands.

The point is not to mechanically pursue “pure REST,” but rather:

Resource-oriented operations follow a unified paradigm, and special business actions have clear and stable naming rules.


3.5 A Contract Is More Than Just URLs

Many projects believe:

Once the URLs are unified, the API standards are complete.

That’s far from enough. A complete API contract should at least include:

Request path
HTTP method
Path parameters
Query parameters
Request Body
Response Body
HTTP Status Code
Error model

Where:

Path Parameter:

Path Parameter

For example:

/agents/{agent_id}

Query Parameter:

Query Parameter

For example:

/agents?page=1&page_size=20

Request Body:

Request Body

The data body sent by the client.

Response Body:

Response Body

The data body returned by the server.

HTTP Status Code:

HTTP Status Code

For example:

200 OK
201 Created
404 Not Found
409 Conflict
500 Internal Server Error

3.6 Query Parameters Must Also Be Unified

Suppose in one system you see:

?page=1&pageSize=20

In another API:

?pageIndex=1&limit=20

And in yet another:

?offset=0&size=20

Each one works.

But the problem is:

The frontend can never remember them all.

miniagent’s Agent list API uses:

page: int = Query(1, ge=1)
page_size: int = Query(20, ge=1, le=100)

And the pagination result is defined as:

class PageResult(BaseModel, Generic[T]):
    total: int
    page: int
    page_size: int
    data: List[T]

So the entire project can uniformly use:

page
page_size
total
data

Instead of reinventing pagination for every module.


3.7 API Responses Must Have a Unified “Envelope”

This is a very important part of Contract First.

The most easily lost scenario is:

User API returns:

{
  "success": true,
  "user": {}
}

Agent API returns:

{
  "code": 0,
  "result": {}
}

Knowledge base API returns:

{
  "status": "ok",
  "data": {}
}

The frontend has to handle each module differently.

This is:

API contract fragmentation.

miniagent defines a unified response model:

class ApiResponse(BaseModel, Generic[T]):
    code: int = 200
    message: str = "success"
    data: Optional[T] = None

So successful responses can be uniformly:

{
  "code": 200,
  "message": "success",
  "data": {}
}

When there’s no data, it can also be:

{
  "code": 200,
  "message": "success"
}

The most important value of this design isn’t saving a few lines of code.

It’s that:

The frontend only needs to learn the response protocol once.


3.8 HTTP Status Codes and Business Response Structures Should Each Serve Their Purpose

When miniagent creates an Agent:

@router.post(
    "",
    response_model=ApiResponse,
    status_code=status.HTTP_201_CREATED
)

Here it uses:

201 Created

To indicate:

The server successfully created a new resource.

While read or update operations typically return:

200 OK

If the resource doesn’t exist, the global exception handler returns:

404

If it already exists:

409

For general bad requests:

400

For internal server errors:

500

miniagent has already uniformly mapped these error types in its global exception handler.

This essentially establishes:

Domain Exception
Unified Exception Handler
HTTP Status Code
Unified ApiResponse

So the business layer doesn’t have to decide on its own:

Should I return 200?
Or 404?
What should the error JSON look like?

3.9 Contract First Truly Solves “Who Has the Final Say”

Without a contract, the development process easily becomes:

Frontend thinks it should be this way
Backend writes it another way
AI generates a third version
Fix during integration testing

With Contract First, it becomes:

Define the API first
Determine the inputs
Determine the outputs
Determine the status codes
Frontend implements
Backend implements
Automated testing

The API becomes:

A protocol that both frontend and backend jointly follow.


3.10 FastAPI Is Naturally Suited for Contract First

FastAPI is:

A framework for building Web APIs based on Python type annotations.

One of its important features is:

It can directly generate API documentation from the type definitions in the code.

For example:

async def create_agent(
    payload: AgentCreate,
):

Here:

AgentCreate

Is itself the input contract.

And:

response_model=ApiResponse

Declares:

The output must conform to the ApiResponse contract.

miniagent also enables during FastAPI initialization:

docs_url="/docs"
redoc_url="/redoc"

FastAPI generates API documentation based on OpenAPI.

OpenAPI:

OpenAPI Specification

Is a machine-readable API description standard.

This way, the API contract doesn’t just exist in people’s heads — it can be directly read by:

Frontend
Testing tools
API debugging tools
Code generators
AI

3.11 RESTful + Schema + ApiResponse Form a Complete API Standard

Using RESTful alone doesn’t solve all problems.

A truly stable API system is usually:

RESTful URL
      +
HTTP Method
      +
Schema
      +
Status Code
      +
Unified Response
      +
Unified Exception

Where Schema can be understood as:

Data structure contract.

For example:

AgentCreate
AgentUpdate
AgentOut

Respectively defining:

What fields are allowed when creating an Agent
What fields are allowed when updating an Agent
What fields are included when returning an Agent

Rather than using a single:

dict

for everything.


4. What Are the Benefits?

After unifying API design, the most obvious change isn’t “prettier code” — it’s the reduction in cognitive overhead for the entire team.

4.1 APIs Become Guessable

Seeing:

GET /agents/12

Without checking documentation, you basically know:

Get Agent 12.

Seeing:

DELETE /agents/12

You can immediately tell:

Delete Agent 12.

This is:

Predictability through consistency.

4.2 Reduced Frontend-Backend Communication Cost

No need to discuss every time a new API is added:

Should it be called getAgentById or queryAgent?

The rules are already established.

4.3 Easier Test Automation

Once APIs are standardized:

POST   Create
GET    Query
PUT    Update
DELETE Delete

Automated testing tools can more easily batch-generate tests.

4.4 More Stable Documentation

FastAPI can generate API documentation directly from:

Router
Schema
Response Model

The API definition itself is part of the documentation.

4.5 Better Suited for AI Programming

This point is especially important.

AI doesn’t need to guess:

What should the API be called?
How should the response be wrapped?
How should errors be handled?
How should pagination be designed?

It just needs to follow the rules.


5. Write API Standards Directly into AI Project Rules

Architecture design alone isn’t enough. If you want AI to comply long-term, you need to write these constraints into the project rules.

For example:

## API Design Rules

1. Follow RESTful resource-oriented API design.
2. URLs must use nouns, not CRUD verbs.
3. Use plural forms for resource names where appropriate.
4. Use GET method to read resources.
5. Use POST method to create resources.
6. Use PUT method to update resources.
7. Use PATCH method for partial state changes.
8. Use DELETE method to delete data.
9. All regular API responses must use the ApiResponse type.
10. Paginated responses must use the PageResult type.
11. Request and response data must use Pydantic schemas.
12. Routers must not return arbitrary dictionary structures.
13. Routers only handle HTTP-related concerns.
14. Business logic should be placed in Service classes.
15. Business errors must be raised as domain exceptions.
16. Do not repeat exception handling within individual routers.
17. Reuse existing API patterns before introducing new endpoints.

Simply put:

URLs only describe resources
HTTP methods express actions
Schema defines inputs and outputs
ApiResponse unifies responses
Domain exceptions are handled uniformly
Routers don't contain business logic

This way, every time AI generates a new API, it already knows:

It cannot redesign a new API style.


6. How to Write Prompts That Actually Work

Don’t just tell AI:

Please help me write Agent CRUD APIs.

CRUD stands for:

Create, Read, Update, Delete

A better prompt is:

Please implement management APIs for the Agent resource.

You must follow the project's existing API contract:

- RESTful resource-oriented URLs
- Reuse the /api/v1/admin/agents path structure
- GET for querying
- POST for creating
- PUT for updating
- DELETE for deleting
- Use PATCH for partial state modifications
- Use existing Pydantic schemas for request parameters
- Use ApiResponse uniformly for return values
- Use PageResult for pagination
- Routers only handle HTTP protocol and dependency injection
- All business logic goes into AgentService
- Use the existing domain exception system for business errors
- Do not redefine exception return formats in Routers
- Before implementing, refer to existing Agent, Tool, and KnowledgeBase API styles in the project

At this point, AI’s task shifts from:

“Help me design and write an API.”

To:

“Build according to the existing contract.”

The stability of the generated code is completely different between the two.


7. Positive Outcome: The API Paradigm Already Established in miniagent

Combining the actual code from miniagent, you can see a relatively clear API chain.

miniagent API Chain

In simple terms:

HTTP Request
FastAPI Router
      ├── Path / Query
      ├── Pydantic Schema
      ├── Permission
      └── HTTP Method
Service
Domain / Repository / Runtime
Result
ApiResponse
HTTP Response

If an exception occurs during business processing:

Service
Domain Exception
Global Exception Handler
   ├── 400
   ├── 404
   ├── 409
   └── 500
ApiResponse

All of these parts have real implementations in miniagent:

  • Routers use GET / POST / PUT / PATCH / DELETE to describe operations.
  • Inputs and outputs are modeled through Pydantic Schemas.
  • All regular responses uniformly use ApiResponse.
  • Pagination results uniformly use PageResult.
  • Business logic is moved from Routers down to Services.
  • Domain exceptions are uniformly converted to HTTP status codes and response structures by the global exception handler.
  • FastAPI automatically provides /docs and /redoc API documentation endpoints.

What ultimately emerges is not a few isolated APIs, but a set of:

Predictable, reusable, extensible API design paradigms that AI can continuously follow.


Final Thoughts

Many people first encounter RESTful and think it’s just:

GET for querying
POST for creating
PUT for updating
DELETE for deleting

But once you get into engineering practice, you realize:

The core value of RESTful is actually a unified language.

And Contract First further solves:

Fixing this language in place before code implementation begins.

For traditional development teams, this reduces communication costs. For AI programming, its value is even greater.

Because without an API contract:

AI
Guesses URLs on its own
Guesses HTTP methods on its own
Guesses parameters on its own
Guesses response structures on its own
Guesses error handling on its own
Project gradually develops multiple API styles

After establishing API standards:

AI
Reads API Rules
Follows RESTful
Reuses Schema
Reuses ApiResponse
Calls Service
Unified exception handling

AI is no longer responsible for “inventing APIs” — it only handles:

Completing implementations according to existing API contracts.

This is one of the most important ideas in architecturally constraining AI programming:

Unify the rules first, then expand generation capabilities.

When API paths, HTTP methods, inputs and outputs, status codes, and exception protocols are all stabilized, the faster AI writes, the less likely the project is to lose control.

That is the true engineering value of RESTful and Contract First.

Open Source Code


🪐 Wishing you good luck 🪐