Generating a TypeScript Axios Client from OpenAPI

A typed HTTP client that stays in lockstep with your spec is the fastest way to stop frontend teams from hand-writing fetch calls. This guide shows the exact OpenAPI Generator invocation to produce a typescript-axios SDK, which --additional-properties actually matter, how to wire it into a build, and how to publish it to npm. It is part of SDK Generation & Changelog Automation and assumes you already have a valid OpenAPI 3.x document.

OpenAPI spec to published npm SDK An OpenAPI spec is generated into a typescript-axios client, compiled with npm build, and published to the npm registry. openapi.yaml OpenAPI 3.x generate -g typescript-axios api.ts + models npm run build .js + .d.ts npm publish @acme/api-client One spec change re-runs the whole pipeline and ships a new SDK version

Problem & Context

Frontend developers consuming your API by hand-writing axios calls produce three recurring problems: request and response types drift from the real API, a renamed field breaks silently at runtime, and every team re-implements auth and error handling. Generating a typescript-axios client solves all three — it ships fully typed request methods, model interfaces, and a single Configuration object for base URL and credentials.

The friction is in the details. The typescript-axios generator has a long list of --additional-properties, and the wrong combination produces a package that will not compile, will not tree-shake, or cannot be published. The three that change the shape of the output most are:

  • npmName — the package name; without it, the generated package.json is incomplete and not publishable.
  • supportsES6 — emits ES6 (import/export, const) instead of ES5, required for modern bundlers and tree-shaking.
  • withSeparateModelsAndApi — splits models and APIs into separate directories so a model change does not invalidate every API import.

Step-by-Step Solution

1. Pin the generator version

npm install -g @openapitools/openapi-generator-cli
openapi-generator-cli version-manager set 7.6.0
openapi-generator-cli version

Expected output:

Did set the version to 7.6.0
7.6.0

2. List the generator’s properties (optional but recommended)

Confirm the property names for the version you pinned — they occasionally change between majors.

openapi-generator-cli config-help -g typescript-axios | grep -E "npmName|supportsES6|withSeparateModelsAndApi"

Expected output (truncated):

	npmName
	    The name under which you want to publish generated npm package.
	supportsES6
	    Generate code that conforms to ES6. (Default: false)
	withSeparateModelsAndApi
	    Put the model and api in separate folders ... (Default: false)

3. Generate the client

openapi-generator-cli generate \
  -i openapi.yaml \
  -g typescript-axios \
  -o ./sdk \
  --additional-properties=npmName=@acme/api-client,npmVersion=1.0.0,supportsES6=true,withSeparateModelsAndApi=true,useSingleRequestParameter=true

Expected output (truncated):

[main] INFO  o.o.codegen.DefaultGenerator - Generated server response code: ...
[main] INFO  o.o.codegen.TemplateManager - writing file ./sdk/api/default-api.ts
[main] INFO  o.o.codegen.TemplateManager - writing file ./sdk/models/index.ts
[main] INFO  o.o.codegen.TemplateManager - writing file ./sdk/package.json
[main] INFO  o.o.codegen.DefaultGenerator - Generation took: ... ms

Confirm the package name and layout:

grep '"name"' ./sdk/package.json && ls ./sdk

Expected output:

  "name": "@acme/api-client",
api  base.ts  common.ts  configuration.ts  index.ts  models  package.json  tsconfig.json

4. Build the package

The generator emits TypeScript and a package.json whose build script runs tsc. Compile to JavaScript plus declaration files before doing anything else.

cd sdk
npm install
npm run build
ls dist

Expected output:

api.js  api.d.ts  configuration.js  configuration.d.ts  index.js  index.d.ts  models

5. Wire it into a consuming app

// usage.ts — in the app that depends on @acme/api-client
import { Configuration, DefaultApi } from '@acme/api-client';

const api = new DefaultApi(
  new Configuration({
    basePath: 'https://api.acme.com',
    accessToken: () => localStorage.getItem('token') ?? '',
  }),
);

const { data } = await api.listOrders();
console.log(data.length);

6. Publish to npm

cd sdk
npm publish --access public

Expected output:

+ @acme/[email protected]

Complete Working Example

A GitHub Actions workflow that regenerates, builds, and publishes the SDK whenever the spec changes — the full pipeline in one self-contained file.

# .github/workflows/publish-sdk.yml
name: Publish TypeScript SDK
on:
  push:
    branches: [main]
    paths: ['openapi.yaml']
jobs:
  generate-build-publish:
    runs-on: ubuntu-latest
    permissions:
      contents: read
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          registry-url: 'https://registry.npmjs.org'

      # Pin the generator so CI output is deterministic.
      - name: Pin OpenAPI Generator
        run: npx --yes @openapitools/openapi-generator-cli version-manager set 7.6.0

      # Generate the typescript-axios client with the properties that shape the output.
      - name: Generate SDK
        run: |
          npx @openapitools/openapi-generator-cli generate \
            -i openapi.yaml \
            -g typescript-axios \
            -o ./sdk \
            --additional-properties=npmName=@acme/api-client,npmVersion=1.0.${{ github.run_number }},supportsES6=true,withSeparateModelsAndApi=true,useSingleRequestParameter=true

      # Compile TypeScript to JS + .d.ts. Publishing source-only would break consumers.
      - name: Build
        working-directory: ./sdk
        run: |
          npm install
          npm run build

      # Publish. NPM_TOKEN must be a repository secret with publish rights.
      - name: Publish to npm
        working-directory: ./sdk
        run: npm publish --access public
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

Gotchas & Edge Cases

supportsES6=false silently breaks tree-shaking. The default emits ES5 with module.exports, which modern bundlers cannot tree-shake, so consumers ship the entire client even if they call one method. Always set supportsES6=true for a browser-bound SDK.

Publishing source instead of dist forces consumers to compile your SDK. If you skip npm run build and publish the raw .ts files, every project that installs the package has to transpile it. Confirm package.json has "main": "dist/index.js" and "types": "dist/index.d.ts", and that dist is included in files or not excluded by .npmignore.

operationId collisions produce duplicate method names. Two operations sharing an operationId generate the same TypeScript method, and the second overwrites the first. Lint the spec for unique operationId values before generating, or the SDK will silently drop a method.

FAQ

Which OpenAPI Generator should I use for a TypeScript Axios client?

Use the typescript-axios generator, which produces a fetch-free client built on the axios HTTP library with full TypeScript types. It is the right choice when your app already uses axios or you want interceptors for auth and retries.

How do I set the npm package name when generating?

Pass npmName through --additional-properties, for example --additional-properties=npmName=@acme/api-client. The generator writes a package.json with that name so the output is ready to build and publish.

Do I need to compile the generated client before publishing?

Yes. The generator emits TypeScript source plus a package.json with a build script, so run npm install and npm run build to produce the .js and .d.ts files before npm publish. Publishing the raw .ts files would force every consumer to compile your SDK.