API client generation tools

I once worked on a project as a frontend developer where the backend developer gave me a file and told me to run it againts a cli and then it generated all the functions to call and invoke apis with full typescript support what could it be?

Ah! From your description, it sounds like the backend developer gave you a OpenAPI/Swagger specification (or something similar like a GraphQL schema) and asked you to generate TypeScript client code from it. Let me break it down:

  1. OpenAPI / Swagger Codegen

    • If the file was a .json or .yaml describing all the API endpoints, request/response schemas, and types, you could run it through a CLI like:

      • openapi-generator-cli

      • swagger-codegen-cli

    • This would generate a full TypeScript client with all API functions typed according to the spec.

    • Example:

      bash
      openapi-generator-cli generate -i api-spec.yaml -g typescript-axios -o ./generated-api
    • After running this, you’d get functions like getUser(), createPost(), etc., all fully typed.

  2. GraphQL Codegen

    • If your backend was GraphQL-based, the file could have been a GraphQL schema. Using a CLI like graphql-codegen would generate TypeScript types and functions to call the GraphQL API.

    • Example:

      bash
      graphql-codegen --config codegen.yml
    • You’d get typed hooks or functions to call queries and mutations.

  3. Other possibilities

    • tRPC: If you used tRPC, the backend could expose procedures and the CLI would generate TypeScript-safe client functions.

    • Swagger/OpenAPI + Axios / Fetch templates is more common in REST APIs.

Most likely: it was OpenAPI/Swagger-based TypeScript client generation.

If you want, I can give a step-by-step minimal example of how that workflow usually looks, including the generated TS functions. Do you want me to do that?

What other options do I have?

Got it—you’re asking about alternatives to generating TypeScript API clients from a backend spec. There are several approaches depending on whether your API is REST or GraphQL, or if you want full type safety. Here’s a structured overview:


1. OpenAPI / Swagger-based generation (REST)

  • How it works: Backend provides an OpenAPI spec (YAML/JSON). CLI generates TS functions and types.

  • Pros: Full types, auto-updates if spec changes, works with Axios/Fetch.

  • Popular tools:

    • openapi-generator-cli

    • swagger-codegen

    • openapi-typescript (just generates types, you write the API calls yourself)

  • When to use: REST API, want strict typing, auto-generated client.


2. GraphQL code generation

  • How it works: Backend exposes GraphQL schema. CLI reads schema + queries/mutations and generates TypeScript types + hooks/functions.

  • Pros: Strong type safety, integrates well with React (Apollo, URQL, React Query), auto-updates types.

  • Popular tools:

    • graphql-codegen

    • Apollo Codegen

  • When to use: GraphQL API.


3. tRPC

  • How it works: Backend exposes typed procedures (functions). Frontend imports a generated client with full type inference. No spec file needed—types flow automatically.

  • Pros: End-to-end type safety without manual generation, very DRY.

  • Cons: Tightly couples frontend and backend, backend must be tRPC.

  • When to use: TypeScript full-stack apps where you control backend.


4. Manual wrapper with axios / fetch + TypeScript types

  • How it works: You write functions yourself calling the API endpoints, and define TypeScript interfaces for request/response.

  • Pros: Simple, flexible, no CLI/tooling.

  • Cons: Manual maintenance, more error-prone.

  • When to use: Small project, no formal spec, or prototype stage.


5. RESTful client generators from RAML / Postman collections

  • How it works: If backend exposes RAML or Postman collection, some tools can generate TS clients.

  • Tools:

    • Postman CLI + postman-to-openapi → then generate TypeScript client

    • raml-to-typescript

  • When to use: API already documented in Postman/RAML.


Summary Recommendation

Approach Type Safety Setup Complexity Best Use Case
OpenAPI/Swagger High Medium REST API with spec
GraphQL Codegen High Medium GraphQL APIs
tRPC Very High Medium Full-stack TS apps
Manual fetch/axios Medium Low Small projects
RAML/Postman → TS client High Medium Existing Postman/RAML documentation

If you want, I can make a flowchart showing which option to pick based on your API type and project needs—it’s a great visual for deciding quickly.

Do you want me to do that?

What about orval

Ah yes! Orval is another excellent option, and it’s very much in the same ecosystem as OpenAPI client generation—but with some modern twists. Let me break it down clearly:


What Orval is

  • Type: OpenAPI (Swagger) client generator for TypeScript/React/Node.

  • Function: Takes an OpenAPI spec (.yaml or .json) and generates fully-typed API clients.

  • Key difference from classic openapi-generator-cli:

    • Orval is TypeScript-first: generated code is idiomatic TS, often easier to work with in modern React/Node apps.

    • Supports React Query out of the box: you can generate hooks directly from the API spec.

    • Generates strongly-typed functions for requests and responses.


How it works

  1. You get a backend OpenAPI spec (e.g., api.yaml).

  2. Install Orval CLI or add it to your package.json:

    bash
    npm install @orval/core --save-dev
  3. Create a config file orval.config.js (or .ts) like this:

    ts
    module.exports = { petstore: { input: './api.yaml', output: { target: './src/api/petstore.ts', client: 'axios', // or 'fetch' mode: 'split' // split by tags } } }
  4. Run:

    bash
    npx orval
  5. You get typed functions (e.g., getPetById(), addPet()) and optionally React Query hooks like useGetPetById().


Pros

  • Generates clean, modern TypeScript code.

  • Built-in support for React Query (or you can use plain axios/fetch).

  • Automatically updates types if API spec changes.

  • Splits APIs by tags for modular imports.


Cons

  • Backend must provide an OpenAPI spec.

  • Smaller ecosystem compared to classic openapi-generator-cli—less tooling if you need very custom templates.


TL;DR: Orval is basically a “TypeScript-first OpenAPI client generator,” perfect if you want modern TS clients or React Query hooks without extra boilerplate.


If you want, I can make a comparison table of Orval vs OpenAPI Generator vs Swagger Codegen to show which is better in which situation. It’s really useful when deciding which to use for a project.

Do you want me to do that?

how to generate openapi file with express backend to later use with orval or openapi generator

Got it! You want to generate an OpenAPI spec from an existing Express backend so you can feed it into Orval or OpenAPI Generator. There are several approaches depending on how much manual work you want vs. automation. I’ll break it down step by step.


1. Using swagger-jsdoc (manual annotation)

This is the most common approach for Express apps.

Install dependencies

bash
npm install swagger-jsdoc swagger-ui-express --save

Setup swagger-jsdoc

Create a swagger.js (or swagger.ts) file:

js
const swaggerJSDoc = require('swagger-jsdoc'); const swaggerUi = require('swagger-ui-express'); const options = { definition: { openapi: '3.0.3', info: { title: 'My API', version: '1.0.0', }, }, // Path to the API docs (your route files) apis: ['./routes/*.js'], }; const swaggerSpec = swaggerJSDoc(options); module.exports = (app) => { app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec)); // Optional: expose raw JSON spec app.get('/swagger.json', (req, res) => res.json(swaggerSpec)); };

Annotate your routes

In your route files, add JSDoc comments:

js
/** * @swagger * /pets: * get: * summary: Get all pets * responses: * 200: * description: List of pets * content: * application/json: * schema: * type: array * items: * type: object * properties: * id: * type: integer * name: * type: string */ router.get('/pets', (req, res) => { res.json([{ id: 1, name: 'Fido' }]); });

Result

  • Running your server exposes:

    • /api-docs → Swagger UI

    • /swagger.json → raw OpenAPI spec JSON (feed this to Orval or OpenAPI Generator)


2. Using tsoa (TypeScript-first approach)

If your Express backend is in TypeScript, tsoa can automatically generate OpenAPI specs from decorators.

Install dependencies

bash
npm install tsoa --save-dev

Add decorators in your controllers

ts
import { Controller, Get, Route } from 'tsoa'; interface Pet { id: number; name: string; } @Route('pets') export class PetsController extends Controller { @Get() public async getPets(): Promise<Pet[]> { return [{ id: 1, name: 'Fido' }]; } }

tsoa config (tsoa.json)

json
{ "entryFile": "src/server.ts", "noImplicitAdditionalProperties": "throw-on-extras", "controllerPathGlobs": ["src/controllers/*.ts"], "spec": { "outputDirectory": "dist", "specVersion": 3 }, "routes": { "basePath": "/", "routesDir": "dist/routes" } }

Generate OpenAPI

bash
npx tsoa spec
  • This creates dist/openapi.json → can be used with Orval or OpenAPI Generator.

  • Optionally, npx tsoa routes generates Express route handlers with type safety.


3. Using express-oas-generator (runtime generation)

  • Auto-generates OpenAPI spec at runtime based on incoming requests.

  • Less manual work, but only generates endpoints you actually hit during testing.

Install

bash
npm install express-oas-generator --save

Setup

js
const express = require('express'); const expressOasGenerator = require('express-oas-generator'); const app = express(); expressOasGenerator.init(app, {}); // optional config // your routes app.get('/pets', (req, res) => { res.json([{ id: 1, name: 'Fido' }]); }); app.listen(3000);
  • After hitting your endpoints, a JSON OpenAPI spec is generated automatically.


Summary / Recommendations

Tool Best For Pros Cons
swagger-jsdoc JS or TS, existing routes Simple, widely used Manual JSDoc annotations
tsoa TypeScript backend Auto-types, spec + route generation Requires TS and decorators
express-oas-generator Quick runtime generation Minimal setup Only generates what you call; less control

After generating the OpenAPI JSON/YAML, you can feed it directly into Orval or OpenAPI Generator to generate a TypeScript client.


If you want, I can make a ready-to-use Express + Orval workflow example, where the OpenAPI file is generated and Orval produces fully-typed API hooks automatically.

Do you want me to do that?

Can I use tsoa with other backend frameworks?

Yes, but with some caveats. tsoa is primarily designed for TypeScript backends, and while it’s most commonly used with Express, it can be used with other frameworks, though it may require extra work. Let me break it down:


1. Supported / Common Frameworks

Express

  • Fully supported.

  • tsoa can generate both OpenAPI spec and Express route handlers automatically.

  • Easiest and most documented.

Koa

  • Not officially supported out-of-the-box.

  • You can still generate the OpenAPI spec using tsoa spec.

  • But you cannot generate Koa-compatible route handlers automatically. You’d have to manually wire the generated controllers.

Fastify

  • Similar to Koa.

  • OpenAPI spec generation works fine.

  • Route generation is manual, but you can use tsoa controllers with Fastify’s routing system.

NestJS

  • tsoa can generate the OpenAPI spec, but NestJS already has its own Swagger module (@nestjs/swagger).

  • Using tsoa is redundant in NestJS because NestJS decorators can already produce OpenAPI 3.0 JSON.


2. What Works Everywhere

Even if your framework is not Express, you can still use tsoa just to generate the OpenAPI spec:

bash
npx tsoa spec
  • This reads your TypeScript controllers and decorators and outputs openapi.json.

  • You can then feed this file into Orval, OpenAPI Generator, or Swagger Codegen to produce a client.

  • You just lose the automatic route generation (npx tsoa routes) that is Express-specific.


3. TL;DR / Recommendation

Framework OpenAPI Spec Route Generation
Express ✅ Yes ✅ Yes
Koa ✅ Yes ❌ No
Fastify ✅ Yes ❌ No
NestJS ✅ Yes ❌ Usually use @nestjs/swagger instead

Key point: tsoa is great as a TypeScript-first OpenAPI generator, even if your backend framework isn’t Express. You just have to handle routing manually in non-Express frameworks.


If you want, I can show a small Koa + tsoa example where you generate an OpenAPI spec from decorators without using Express at all. This can then go straight into Orval.

Do you want me to do that?

What do you mean by route generation?

Ah! Let me clarify carefully—this is an important distinction. In tsoa, “route generation” refers to automatically generating the backend code that wires your controllers to HTTP endpoints, not the frontend API calls.


1. Route Generation in tsoa

When you define a controller with methods in tsoa, for example:

ts
import { Controller, Get, Route } from 'tsoa'; @Route('pets') export class PetsController extends Controller { @Get() public async getPets() { return [{ id: 1, name: 'Fido' }]; } }

tsoa can do two things:

  1. Generate OpenAPI Spec

    • Reads your decorators (@Route, @Get, etc.)

    • Outputs a JSON or YAML OpenAPI file describing all endpoints, request/response types

    • This is framework-independent

  2. Generate Routes / Route Handlers (Express-specific)

    • Creates code like this under the hood:

ts
import express from 'express'; import { PetsController } from '../controllers/petsController'; const router = express.Router(); router.get('/pets', async (req, res, next) => { try { const result = await new PetsController().getPets(); res.json(result); } catch (err) { next(err); } }); export default router;
  • This is called route generation because it automatically wires your TypeScript controllers to Express routes.

  • You don’t have to manually write app.get('/pets', …) everywhere.


2. Why this matters

  • In Express, you can just run:

bash
npx tsoa routes
  • And you get a ready-to-use router file.

  • In other frameworks (Fastify, Koa), this auto-generation doesn’t exist. You’d still need to manually connect your controllers to routes:

ts
// Koa example router.get('/pets', async (ctx) => { ctx.body = await new PetsController().getPets(); });
  • But the OpenAPI spec generation still works, so you can feed it to Orval or OpenAPI Generator.


TL;DR:

  • OpenAPI spec generation → generates the JSON/YAML describing your API (framework-independent)

  • Route generation → generates backend code that hooks your controllers into HTTP endpoints automatically (Express-only)


If you want, I can draw a diagram showing controllers → route generation → OpenAPI spec → Orval client to make this whole flow crystal clear. It’s very helpful to visualize.

Do you want me to do that?

give me nestjs as well with its own nest swagger

Absolutely! NestJS has its own built-in Swagger integration that’s slightly different from tsoa but serves a similar purpose: generating an OpenAPI spec directly from your controllers and decorators. Here’s a full breakdown:


1. Install NestJS Swagger dependencies

bash
npm install @nestjs/swagger swagger-ui-express
  • @nestjs/swagger → decorators and helpers for NestJS

  • swagger-ui-express → exposes Swagger UI


2. Add Swagger setup in your main.ts

ts
import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; async function bootstrap() { const app = await NestFactory.create(AppModule); const config = new DocumentBuilder() .setTitle('My API') .setDescription('API documentation for my NestJS app') .setVersion('1.0') .build(); const document = SwaggerModule.createDocument(app, config); SwaggerModule.setup('api-docs', app, document); await app.listen(3000); } bootstrap();
  • Access Swagger UI at http://localhost:3000/api-docs

  • Raw OpenAPI spec JSON available via SwaggerModule.createDocument() if you want to save it to a file.


3. Annotate your controllers and DTOs

NestJS uses decorators (@Controller, @Get, @Post, etc.) plus Swagger decorators (@ApiTags, @ApiProperty) for OpenAPI metadata.

ts
import { Controller, Get } from '@nestjs/common'; import { ApiTags, ApiProperty } from '@nestjs/swagger'; class Pet { @ApiProperty() id: number; @ApiProperty() name: string; } @ApiTags('pets') @Controller('pets') export class PetsController { @Get() getPets(): Pet[] { return [{ id: 1, name: 'Fido' }]; } }
  • @ApiTags('pets') → groups endpoints in Swagger UI

  • @ApiProperty() → annotates DTO properties for the OpenAPI spec


4. Generate a raw OpenAPI JSON file (optional)

If you want the spec for Orval or OpenAPI Generator:

ts
import { writeFileSync } from 'fs'; const document = SwaggerModule.createDocument(app, config); writeFileSync('./openapi.json', JSON.stringify(document, null, 2));
  • This JSON/YAML file can now be fed into Orval, OpenAPI Generator, or any other client generator.


5. Comparison with tsoa

Feature tsoa NestJS Swagger
Framework-specific route gen Express only Built-in NestJS routing
Decorators for controllers @Route, @Get etc. @Controller, @Get + @Api*
OpenAPI Spec generation
TypeScript-first
Frontend client integration Orval / OpenAPI Generator Orval / OpenAPI Generator

TL;DR:

  • NestJS has native Swagger support, generating OpenAPI specs directly from controllers/DTOs.

  • You can export that spec to JSON/YAML and feed it to Orval to generate a fully-typed frontend client.

  • Unlike tsoa, NestJS already handles route generation internally, so you don’t need an extra step.


If you want, I can make a diagram showing how Express + tsoa vs NestJS + Nest Swagger → Orval client works side by side. This helps compare workflows visually.

Do you want me to do that?

how long does it take to learn nest for an experienced express dev?

If you’re already an experienced Express developer, learning NestJS is mostly about learning the framework’s conventions and architecture, rather than the basics of Node.js or HTTP. Here’s a breakdown:


1. Core Learning Areas

  1. Module-based architecture

    • NestJS organizes code into Modules, Controllers, and Providers.

    • Express developers are used to flat routes/middleware; Nest encourages a structured, modular approach.

  2. Decorators & TypeScript features

    • @Controller, @Get, @Injectable, @Module, @Body(), etc.

    • If you’re comfortable with TypeScript, decorators are easy to grasp.

  3. Dependency Injection (DI) system

    • NestJS has a built-in DI container for services/providers.

    • Express devs usually manage dependencies manually, so DI is the biggest conceptual shift.

  4. Pipes, Guards, Interceptors, Middleware

    • These are Nest’s equivalents of validation, authorization, and custom request/response handling.

    • Similar to Express middleware, just more structured.

  5. Swagger/OpenAPI integration

    • Optional, but straightforward once controllers and DTOs are typed.

  6. Testing & CLI tools

    • @nestjs/testing for unit/e2e tests.

    • nest g module/controller/service for scaffolding.


2. Estimated Learning Timeline

Assuming you’re already comfortable with Express and TypeScript:

Stage Time Estimate What You’ll Learn
Basic Nest app 1–2 days Controllers, modules, routing
DI & Services 2–3 days Providers, dependency injection, singleton/scoped services
Middleware, Pipes, Guards, Interceptors 2–3 days Validation, auth, logging, error handling
Swagger / OpenAPI integration 1 day Auto-generating API docs
Testing & CLI tools 1–2 days Unit/e2e tests, scaffolding modules/controllers

Total: ~1–2 weeks to become productive for standard API development.

Most Express devs find the initial learning curve steep because of the structured patterns and decorators, but once you “think in modules,” development speeds up compared to vanilla Express.


3. Tips to Accelerate Learning

  • Start by migrating a small Express project to Nest. Seeing the differences firsthand is faster than tutorials.

  • Focus on controllers + services first, then gradually add middleware, guards, and interceptors.

  • Use the Nest CLI; it saves a lot of boilerplate and enforces best practices.

  • Leverage the official NestJS docs—they’re well-structured for Express devs.


If you want, I can make a direct mapping cheat sheet: “Express → NestJS equivalents” showing middleware, routes, DI, etc. That usually cuts the learning curve in half for experienced devs.

Do you want me to make that cheat sheet?