# ACAI — Chapter 39: API Architecture, Request Processing & Secure Service Boundaries

![Post cover](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/3ujzb03ei5i52xmvq3y3.png align="center")

  

## 39.1 Introduction

Chapter 38 established authentication, sessions, authorization, ownership, and permission boundaries.

The next layer is the API architecture.

The API is the controlled communication boundary between the client application and the server-side platform.

A well-designed API must provide more than endpoints. It must establish consistent rules for:

*   request validation;
    
*   authentication;
    
*   authorization;
    
*   routing;
    
*   business logic;
    
*   database access;
    
*   error handling;
    
*   rate limiting;
    
*   logging;
    
*   request tracing;
    
*   response formatting;
    
*   versioning;
    
*   security controls.
    

The fundamental architecture is:

```text
Client
  │
  ▼
HTTP Request
  │
  ▼
API Router
  │
  ▼
Request Validation
  │
  ▼
Authentication
  │
  ▼
Authorization
  │
  ▼
Rate Limit / Policy
  │
  ▼
Service Layer
  │
  ▼
Repository
  │
  ▼
Database / External Service
  │
  ▼
Response
```

* * *

# 39.2 API Design Principles

The API should follow several principles.

### Principle 1 — Explicit boundaries

Every endpoint should have a clearly defined purpose.

### Principle 2 — Server-side trust

The server must independently validate security-sensitive information.

### Principle 3 — Consistent responses

Clients should not have to guess how different endpoints behave.

### Principle 4 — Small responsibilities

A route should coordinate an operation rather than contain the entire application's business logic.

### Principle 5 — Observability

Important requests should be traceable without exposing secrets.

* * *

# 39.3 API Directory Structure

A Next.js App Router implementation can use:

```text
src/
└── app/
    └── api/
        ├── auth/
        │   ├── login/
        │   │   └── route.ts
        │   └── logout/
        │       └── route.ts
        │
        ├── users/
        │   └── route.ts
        │
        ├── projects/
        │   ├── route.ts
        │   └── [id]/
        │       └── route.ts
        │
        ├── conversations/
        │   ├── route.ts
        │   └── [id]/
        │       └── route.ts
        │
        └── files/
            ├── route.ts
            └── [id]/
                └── route.ts
```

The structure should reflect the application's resource model.

* * *

# 39.4 Resource-Oriented API

The initial API can be organized around resources.

For example:

```text
/api/users
/api/projects
/api/conversations
/api/messages
/api/files
```

Individual resources can use:

```text
/api/projects/{projectId}
/api/conversations/{conversationId}
/api/files/{fileId}
```

This makes the API easier to understand.

* * *

# 39.5 HTTP Methods

Typical operations are:

| Method | Purpose |
| --- | --- |
| GET | Retrieve data |
| POST | Create a resource or initiate an operation |
| PATCH | Partially update a resource |
| PUT | Replace a resource where appropriate |
| DELETE | Delete or deactivate a resource |

For example:

```text
GET    /api/projects
POST   /api/projects
GET    /api/projects/{id}
PATCH  /api/projects/{id}
DELETE /api/projects/{id}
```

The exact semantics should remain consistent across the application.

* * *

# 39.6 Request Lifecycle

A request should pass through a predictable lifecycle.

```text
HTTP Request
     │
     ▼
Request ID
     │
     ▼
Parse Request
     │
     ▼
Validate Input
     │
     ▼
Authenticate
     │
     ▼
Authorize
     │
     ▼
Rate Limit
     │
     ▼
Service
     │
     ▼
Repository
     │
     ▼
External Systems
     │
     ▼
Response
     │
     ▼
Logging
```

Not every endpoint needs every step, but security-sensitive endpoints should have appropriate controls.

* * *

# 39.7 Request IDs

Every important request should have a correlation identifier.

Example:

```text
requestId = "req_..."
```

The request ID can appear in server logs.

A simplified flow:

```text
Client Request
      │
      ▼
Generate Request ID
      │
      ├──────────────► Application Logs
      │
      ▼
Process Request
      │
      ▼
Response
```

If a user reports a problem, support personnel can use the request ID to locate relevant server-side logs without exposing internal details.

* * *

# 39.8 Input Validation

Never assume that JSON submitted by the client is valid.

Example request:

```json
{
  "name": "Research Project"
}
```

The server should verify:

```text
name exists
name is a string
name length is acceptable
name does not violate application rules
```

Validation should happen before business logic.

* * *

# 39.9 Validation Layer

A reusable validation module can be created:

```text
src/lib/validation.ts
```

For example, using a schema-validation library:

```ts
import { z } from "zod";

export const createProjectSchema =
  z.object({
    name: z
      .string()
      .trim()
      .min(1)
      .max(200),

    description: z
      .string()
      .trim()
      .max(2000)
      .optional(),
  });
```

The API can then validate:

```ts
const body =
  createProjectSchema.parse(
    await request.json()
  );
```

The exact validation library is an implementation choice, but the principle remains the same.

* * *

# 39.10 Validation vs Sanitization

Validation asks:

> Is this input acceptable?

Sanitization asks:

> How should potentially unsafe or unwanted input be normalized?

These should not be treated as interchangeable.

For example:

```text
Validation
"Is the project name a valid string?"

Output handling
"How should this string be displayed safely?"
```

Every output context must be handled appropriately.

* * *

# 39.11 Authentication Middleware

Protected routes should resolve the current user.

Conceptually:

```ts
const user = await requireUser();
```

If no authenticated identity exists:

```text
401 Unauthorized
```

The route should stop processing.

* * *

# 39.12 Authorization Middleware

Authentication does not guarantee access.

For project operations:

```ts
const project =
  await getUserProject(
    user.id,
    projectId
  );
```

If the user does not own or otherwise have permission to access the project:

```text
403 Forbidden
```

or an intentionally non-disclosing resource response, depending on the application's security policy.

* * *

# 39.13 API Response Contract

Responses should follow a consistent structure.

Successful response:

```json
{
  "data": {
    "id": "project-id",
    "name": "Research Project"
  }
}
```

Error response:

```json
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid request."
  }
}
```

The client can then reliably process the response.

* * *

# 39.14 Error Codes

Instead of relying only on human-readable messages, APIs can provide stable error codes.

Examples:

```text
VALIDATION_ERROR
UNAUTHORIZED
FORBIDDEN
NOT_FOUND
CONFLICT
RATE_LIMITED
INTERNAL_ERROR
SERVICE_UNAVAILABLE
```

The client can use the code for behavior while the message remains user-friendly.

* * *

# 39.15 HTTP Status Codes

A reasonable baseline is:

```text
200 OK
201 Created
204 No Content
400 Bad Request
401 Unauthorized
403 Forbidden
404 Not Found
409 Conflict
429 Too Many Requests
500 Internal Server Error
503 Service Unavailable
```

The application should use them consistently.

* * *

# 39.16 Project Creation Endpoint

A simplified endpoint:

```ts
import { NextResponse } from "next/server";
import { requireUser } from "@/lib/auth";
import { createProject } from "@/services/project.service";

export async function POST(
  request: Request
) {
  try {
    const user = await requireUser();

    const body = await request.json();

    const project =
      await createProject(
        user.id,
        body
      );

    return NextResponse.json(
      {
        data: project,
      },
      {
        status: 201,
      }
    );
  } catch (error) {
    console.error(error);

    return NextResponse.json(
      {
        error: {
          code: "INTERNAL_ERROR",
          message:
            "Unable to create project.",
        },
      },
      {
        status: 500,
      }
    );
  }
}
```

In a production implementation, validation and error classification should be explicit rather than relying on one generic catch block.

* * *

# 39.17 Service Layer

The service should own the business operation.

Example:

```ts
export async function createProject(
  userId: string,
  input: {
    name: string;
    description?: string;
  }
) {
  if (!input.name.trim()) {
    throw new Error(
      "Project name is required."
    );
  }

  return prisma.project.create({
    data: {
      name: input.name.trim(),
      description:
        input.description?.trim(),
      ownerId: userId,
    },
  });
}
```

A more mature implementation would separate validation from business logic and map domain errors to stable API errors.

* * *

# 39.18 Repository Boundary

The service should not necessarily contain raw SQL or large ORM queries.

Instead:

```text
Service
   │
   ▼
Repository
   │
   ▼
Prisma
```

Example:

```ts
export async function
createProjectRecord(data: {
  name: string;
  description?: string;
  ownerId: string;
}) {
  return prisma.project.create({
    data,
  });
}
```

This keeps data-access logic reusable.

* * *

# 39.19 API Versioning

As the platform evolves, API contracts may change.

A versioned design can use:

```text
/api/v1/projects
/api/v1/conversations
```

Later:

```text
/api/v2/projects
```

Versioning should not be added everywhere without a reason.

The purpose is to provide controlled compatibility when breaking changes become necessary.

* * *

# 39.20 Idempotency

Some operations can accidentally be repeated.

For example:

```text
Client sends request
       ↓
Network timeout
       ↓
Client does not know whether server succeeded
       ↓
Client retries
```

Without protection, a duplicate operation could occur.

For suitable operations, an idempotency key can be used:

```text
Idempotency-Key: unique-operation-id
```

The server can associate the key with the operation result.

This is particularly valuable for operations involving external side effects.

* * *

# 39.21 Rate Limiting

APIs should not assume that every client sends requests at a reasonable rate.

Rate limiting can be applied at different levels:

```text
IP
User
Session
API key
Endpoint
Resource
```

For example:

```text
Authentication endpoint
      ↓
Strict rate limit

Normal read endpoint
      ↓
Moderate limit

Expensive AI operation
      ↓
Usage-based limit
```

The exact thresholds should be determined through capacity planning and abuse testing.

* * *

# 39.22 AI Operation Limits

AI generation requests may consume significantly more resources than ordinary database queries.

Therefore:

```text
GET /projects
```

and

```text
POST /ai/generate
```

should not necessarily share the same rate policy.

An AI request may require:

```text
Authentication
+
Authorization
+
Usage quota
+
Rate limit
+
Input validation
+
Model policy
+
Budget check
```

* * *

# 39.23 Usage Accounting

The API layer should eventually connect AI requests to usage records.

Conceptually:

```text
AI Request
   │
   ├── User
   ├── Project
   ├── Model
   ├── Request ID
   ├── Input units
   ├── Output units
   └── Status
```

This allows the system to monitor resource consumption.

* * *

# 39.24 API and AI Safety

AI endpoints require additional controls because the request is not necessarily a simple CRUD operation.

An AI request may involve:

```text
User input
     ↓
Prompt construction
     ↓
Retrieved documents
     ↓
Memory
     ↓
Model
     ↓
Generated output
     ↓
Tool execution
```

Each stage can become a security boundary.

Therefore the API should not simply forward arbitrary user content to privileged systems.

* * *

# 39.25 Prompt and Context Boundary

A request such as:

```text
"Use my documents to answer this question."
```

should result in controlled retrieval:

```text
Authenticated User
       ↓
Authorized Project
       ↓
Authorized Documents
       ↓
Relevant Context
       ↓
Model
```

The API must not retrieve documents merely because their identifiers were supplied by the client.

* * *

# 39.26 Tool Invocation Boundary

Future agent APIs may request tools.

A safer model is:

```text
Agent Request
      ↓
Tool Requested
      ↓
Permission Check
      ↓
Policy Check
      ↓
Optional Human Approval
      ↓
Tool Execution
```

The model itself should not bypass application authorization.

* * *

# 39.27 External Service Failures

The API may depend on:

```text
Database
Object storage
Vector store
Model provider
Email service
Payment service
Queue
```

Any of these can fail.

The API should distinguish:

```text
Client error
```

from:

```text
Temporary infrastructure failure
```

For example:

```text
503 Service Unavailable
```

may be appropriate when a required downstream service is temporarily unavailable.

* * *

# 39.28 Timeout Management

External requests should not be allowed to hang indefinitely.

Conceptually:

```text
API Request
     │
     ▼
External Service
     │
     ├── Success
     │
     ├── Failure
     │
     └── Timeout
```

Timeout behavior should be explicit.

Long-running AI tasks may be better represented as asynchronous jobs rather than holding an HTTP request open indefinitely.

* * *

# 39.29 Synchronous vs Asynchronous Operations

Simple operations:

```text
Create project
Update profile
Read conversation
```

can generally be synchronous.

Long-running operations:

```text
Large document processing
Video generation
Batch analysis
Embedding generation
Complex agent workflow
```

may be better represented as jobs.

Example:

```text
POST /api/jobs
        │
        ▼
Job Created
        │
        ▼
202 Accepted
        │
        ▼
Background Worker
        │
        ▼
Job Complete
```

The client can then poll or receive a notification.

* * *

# 39.30 API Job Model

A future job record can contain:

```text
id
userId
projectId
type
status
progress
input
result
error
createdAt
startedAt
completedAt
```

Example status lifecycle:

```text
PENDING
   ↓
QUEUED
   ↓
RUNNING
   ↓
COMPLETED
```

Alternative path:

```text
RUNNING
   ↓
FAILED
```

or:

```text
RUNNING
   ↓
CANCELLED
```

* * *

# 39.31 Cancellation

Long-running operations should support cancellation where technically feasible.

Example:

```text
POST /api/jobs/{id}/cancel
```

The server should verify:

```text
Authenticated user
        ↓
Job ownership/access
        ↓
Job is cancellable
        ↓
Cancellation requested
```

* * *

# 39.32 Logging

API logging should provide enough information to diagnose failures.

Useful fields include:

```text
requestId
timestamp
route
method
status
duration
user identifier
project identifier
error code
```

Avoid logging:

```text
passwords
session tokens
API keys
private credentials
unnecessary personal data
full sensitive documents
```

* * *

# 39.33 Structured Logging

Instead of:

```text
Something went wrong.
```

a structured log might conceptually contain:

```json
{
  "event": "api_request_failed",
  "requestId": "req_example",
  "route": "/api/projects",
  "method": "POST",
  "status": 500,
  "errorCode": "DATABASE_ERROR"
}
```

Structured logs are easier to search and analyze.

* * *

# 39.34 Monitoring Metrics

Important API metrics include:

```text
Request count
Error rate
Latency
95th percentile latency
99th percentile latency
Rate-limit events
Authentication failures
Database errors
External service failures
AI job failures
```

Monitoring should focus on operational signals rather than collecting unnecessary user content.

* * *

# 39.35 Security Events

Certain events should receive elevated attention:

```text
Repeated authentication failures
Large numbers of authorization failures
Unusual API usage
Repeated invalid requests
Unexpected administrative operations
Abnormal agent/tool activity
```

These events can feed a security monitoring system.

* * *

# 39.36 API Testing

Every endpoint should have tests covering:

### Happy path

```text
Valid request
Authenticated user
Correct permissions
Expected result
```

### Invalid input

```text
Missing field
Wrong type
Too long
Invalid identifier
```

### Authentication

```text
No session
Expired session
Revoked session
```

### Authorization

```text
Wrong owner
Insufficient role
Forbidden operation
```

### Reliability

```text
Database unavailable
External service timeout
Duplicate request
Rate limit exceeded
```

* * *

# 39.37 Example API Test Matrix

| Scenario | Expected |
| --- | --- |
| Valid project creation | 201 |
| Missing project name | 400 |
| Anonymous project creation | 401 |
| Unauthorized project access | 403/404 according to policy |
| Duplicate operation | Controlled result |
| Excessive requests | 429 |
| Database unavailable | 503/500 according to failure policy |

* * *

# 39.38 API Security Checklist

```text
[ ] Every protected endpoint authenticates the user
[ ] Authorization is checked separately
[ ] Ownership is verified
[ ] Request bodies are validated
[ ] Query parameters are validated
[ ] Client-supplied identity is not trusted
[ ] Sensitive errors are not exposed
[ ] Rate limiting exists where necessary
[ ] Expensive operations have usage controls
[ ] External requests have timeouts
[ ] Long-running work uses jobs where appropriate
[ ] Request IDs are available
[ ] Security events are logged
[ ] Secrets are excluded from logs
[ ] API contracts are documented
```

* * *

# 39.39 Complete Request Model

The complete architecture now becomes:

```text
                         CLIENT
                           │
                           ▼
                    HTTP REQUEST
                           │
                           ▼
                     REQUEST ID
                           │
                           ▼
                     VALIDATION
                           │
                           ▼
                    AUTHENTICATION
                           │
                           ▼
                    AUTHORIZATION
                           │
                           ▼
                    RATE / USAGE
                       CONTROL
                           │
                           ▼
                     SERVICE LAYER
                           │
              ┌────────────┴────────────┐
              │                         │
        Repository                 External API
              │                         │
              ▼                         ▼
          PostgreSQL              Model / Storage
              │                         │
              └────────────┬────────────┘
                           │
                           ▼
                        RESULT
                           │
                           ▼
                    RESPONSE CONTRACT
                           │
                           ▼
                         CLIENT
```

* * *

# 39.40 Relationship to Previous Chapters

The architecture now forms a continuous chain.

Chapter 34:

```text
Database Architecture
```

Chapter 35:

```text
Database Schema
```

Chapter 36:

```text
Database Stack
```

Chapter 37:

```text
Database Implementation
```

Chapter 38:

```text
Authentication & Authorization
```

Chapter 39:

```text
API Architecture
```

Together:

```text
Data
 ↓
Database
 ↓
Identity
 ↓
Permission
 ↓
API
 ↓
Services
```

This is the foundation for the application-level AI systems that follow.

* * *

# 39.41 Conclusion

The API layer is not merely a collection of URLs.

It is a controlled boundary where:

*   identity is established;
    
*   permissions are evaluated;
    
*   inputs are validated;
    
*   business rules are applied;
    
*   data is accessed;
    
*   external services are coordinated;
    
*   errors are classified;
    
*   usage is measured;
    
*   security events are observed.
    

The resulting principle is:

```text
Never trust the request merely because
it reached the API.
```

The server must independently establish:

```text
Who?
What?
Which resource?
Which permission?
Which operation?
Which limits?
Which policy?
```

Only after these checks should the operation execute.

The next chapter will build on this foundation with **file and object-storage architecture**, including secure uploads, metadata, ownership, processing pipelines, file validation, storage keys, signed access, lifecycle management, and the security boundaries required for AI document and media workflows.

**END OF CHAPTER 39**
