API Docs and Clients

Assegai can derive docs, exports, and generated clients from the same API structure that powers your application.

If your controllers define routes, your DTOs define request shape, and your validation attributes define constraints, the framework already has most of the information needed to describe the contract of your API.

That is the core idea behind Assegai's API docs and client workflow.

Instead of maintaining several parallel artifacts by hand, you can let the application describe itself once, then turn that structure into useful outputs such as:

  • Swagger UI
  • OpenAPI
  • Postman collections
  • a typed TypeScript client

This is one of the clearest examples of what Assegai is trying to do as a framework: keep your application structured enough that the structure itself becomes useful.

Why this matters

A lot of backend projects still repeat the same work in several places:

  • routes are defined in code
  • request shape is defined separately
  • validation rules live elsewhere
  • docs are written manually
  • frontend clients are maintained by hand

That duplication creates drift.

As soon as the API changes, one or more of those layers usually falls behind.

Assegai reduces that problem by deriving the contract from the application code you already maintain.

The result is a workflow that is easier to trust, easier to inspect, and easier to keep in sync over time.

A quick working path

The easiest way to understand this feature is to use it in a normal feature workflow.

assegai new blog-api
cd blog-api
assegai g r posts
assegai serve

A generated resource gives you a structured feature surface immediately.

Typical routes include:

GET     /posts
GET     /posts/:id
POST    /posts
PATCH   /posts/:id
DELETE  /posts/:id

Once the application is running, you can inspect the contract through the documentation endpoints and export it for other tools.

That means the same feature you generated for the backend can also become a documented and consumable API surface.

What Assegai exposes

In a normal development setup, Assegai can expose:

  • /docs for Swagger UI
  • /openapi.json for the OpenAPI document

These are two different views of the same contract.

Use /docs when you want a browser-friendly interface for exploring endpoints.

Use /openapi.json when you want the structured contract for tooling, exports, or client generation.

This is especially helpful once the API grows beyond one or two endpoints and you need a reliable way to inspect what the application actually expects and returns.

Where the contract comes from

The docs and client workflow works because Assegai already encourages a structured application shape:

  • controllers describe routes and methods
  • DTOs describe request shape
  • validation attributes describe constraints
  • typed parameters and return paths add more metadata

That means the framework can derive more than runtime behavior. It can also derive a usable API contract.

This is the most important idea to keep in mind:

Your application code is not only implementation. It is also contract metadata.

A practical example

A controller like this already says quite a lot about the API:

<?php

use Assegai\Core\Attributes\Controller;
use Assegai\Core\Attributes\Http\Body;
use Assegai\Core\Attributes\Http\Get;
use Assegai\Core\Attributes\Http\Post;
use Assegai\Core\Attributes\Param;

#[Controller('posts')]
readonly class PostsController
{
  public function __construct(private PostsService $postsService)
  {
  }

  #[Get]
  public function findAll(): mixed
  {
    return $this->postsService->findAll();
  }

  #[Get(':id')]
  public function findById(#[Param('id')] int $id): mixed
  {
    return $this->postsService->findById($id);
  }

  #[Post]
  public function create(#[Body] CreatePostDTO $dto): mixed
  {
    return $this->postsService->create($dto);
  }
}

And a DTO like this adds even more shape to the contract:

<?php

use Assegai\Core\Attributes\Injectable;
use Assegai\Validation\Attributes\IsNotEmpty;
use Assegai\Validation\Attributes\IsString;

#[Injectable]
class CreatePostDTO
{
  #[IsString]
  #[IsNotEmpty]
  public string $title = '';

  #[IsString]
  public string $body = '';
}

From this structure, the framework can already infer several useful parts of the API contract:

  • the route path
  • the HTTP method
  • the body type
  • required vs optional fields
  • validation constraints
  • route parameter expectations

That is what makes the docs and export workflow feel cohesive instead of bolted on.

Inspect docs during development

The fastest way to use this feature is in the running app.

Start the server:

assegai serve

Then open:

/docs
/openapi.json

This gives you a tight feedback loop while building.

As soon as your controllers and DTOs are in place, you can inspect the contract in the browser without writing a separate documentation layer by hand.

That makes local iteration much smoother, especially when several endpoints or DTOs are evolving at the same time.

Export the contract when needed

The running app is useful during development, but sometimes the contract needs to exist outside the app.

That is where export commands help.

Export OpenAPI

assegai api:export openapi

Use this when you want to:

  • commit the contract to the repository
  • publish the spec elsewhere
  • feed another tool
  • compare API versions over time

Export Postman

assegai api:export postman

Use this when you want to:

  • hand QA a ready-to-run collection
  • support manual testing quickly
  • share the API with frontend or partner teams
  • provide an easier entry point for non-backend contributors

The practical value here is consistency: these exports come from the same contract source as the application itself.

Generate a TypeScript client

One of the strongest follow-up steps is client generation.

assegai api:client typescript

This lets the backend contract generate a typed frontend-facing client from the same source of truth.

That helps reduce another common source of drift:

  • backend request shapes change
  • frontend types remain stale
  • integrations break in subtle ways

A generated TypeScript client helps keep the contract explicit on both sides.

It is especially useful when:

  • a frontend application consumes the API
  • several consumers depend on the same endpoints
  • you want to reduce repeated interface definitions
  • you want a tighter backend-to-frontend workflow

A realistic day-to-day workflow

A strong API-first loop in Assegai looks like this:

  1. generate a resource
  2. update the DTOs
  3. add validation rules
  4. implement the provider logic
  5. run the app
  6. inspect /docs
  7. inspect /openapi.json
  8. export OpenAPI or Postman when needed
  9. generate a TypeScript client when another app will consume the API

This is one of the most compelling examples of Assegai's product story.

You are not only generating routes and handlers.

You are generating a structured feature slice that can also power docs, exports, and consumers of the API.

What makes the generated output better

The quality of the generated docs and clients depends on the quality of the structure you give the framework.

The outputs become more useful when:

  • controllers define routes clearly
  • DTOs describe request shape precisely
  • validation rules are applied intentionally
  • params are typed and constrained
  • feature boundaries stay modular and explicit

In practice, this means the same architectural discipline that improves the codebase also improves the generated contract.

That is a useful reinforcing effect.

Where this fits in team workflows

This workflow becomes more valuable as more people interact with the API.

Backend developers

Need a quick way to verify that the code and contract still match.

Frontend developers

Need a client or a reliable spec.

QA

Needs a fast way to inspect and exercise endpoints.

Integration teams or partners

Need a stable description of how the API behaves.

That is why this feature matters strategically. It is not only about nicer documentation. It is about reducing friction between the backend and the people or systems consuming that backend.

Practical guidance

Use /docs when:

  • you are iterating locally
  • you want a human-readable contract view
  • you want to inspect endpoints quickly

Use OpenAPI export when:

  • the spec needs to leave the running app
  • another tool needs the contract
  • you want a versioned artifact

Use Postman export when:

  • manual testing is the main need
  • a mixed team needs easy access
  • you want a shared collection quickly

Use TypeScript client generation when:

  • a frontend app will consume the API
  • you want to reduce duplicate types
  • you want a stronger backend-to-frontend contract loop

A common mistake to avoid

Do not treat docs and clients as magic outputs that compensate for unclear application structure.

They depend on the structure you give the framework.

If controllers are vague, DTOs are incomplete, or request contracts are loosely defined, the generated outputs will be weaker too.

The best way to improve the docs and clients is usually the same way you improve the application itself:

  • make contracts explicit
  • keep handlers clear
  • use DTOs intentionally
  • validate request data at the edge

Practical takeaway

Assegai's docs and client tooling work best when you lean into the framework's structure:

  • controllers for transport
  • DTOs for contracts
  • validation attributes for request rules
  • modular boundaries for clean feature organization

The better your app expresses its shape, the more the framework can derive from it.

That is the real value here.

You are not documenting the API twice. You are letting the application describe itself once, then turning that structure into useful outputs.