Customizing OpenAPI Generator Templates

When the SDKs that OpenAPI Generator produces are 95% right but a license header is missing, a method name reads awkwardly, or the README is generic, the answer is not to hand-patch generated code on every run. It is to override the Mustache templates the generator renders from. This guide is part of SDK Generation & Changelog Automation and shows how to extract the built-in templates, override only the files you need, and protect hand-edited output — without forking the whole template set.

How OpenAPI Generator resolves templates For each output file the generator first looks in your custom template directory, and falls back to the built-in template when no override exists. Output file api.ts Your -t directory? api.mustache present Built-in template (fallback) Rendered SDK api.ts written Override wins when present; otherwise the default renders

Problem & Context

A team generating a typescript-axios client wants every source file to carry a copyright header and the generated README.md to point at their internal registry instead of the boilerplate Markdown. The naive fixes all rot quickly:

  • Editing generated files by hand means the changes vanish the next time CI regenerates the SDK.
  • Forking the entire template directory (dozens of .mustache files per generator) means you inherit responsibility for keeping all of them in sync when you bump from one 7.x release to the next.

The generator is designed for exactly this. For every output file it renders, it first looks in the directory you pass with -t (--template-dir). If a matching .mustache file exists there, your version is used; if not, the built-in template ships the default. So the correct strategy is: override only the handful of files you actually change, and let the generator own everything else.

Step-by-Step Solution

1. Install and pin the generator

Version drift in templates is real — internal Mustache variable names and partial layouts change between major versions. Pin to a 7.x release so your overrides stay valid.

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. Extract the default templates for your generator

Use the author template subcommand to copy the exact built-in templates for the generator you target. This is the only reliable way to see the real variable names and partials — never guess them.

openapi-generator-cli author template \
  -g typescript-axios \
  -o openapi-templates

Expected output (truncated):

[main] INFO  o.o.codegen.TemplateManager - writing file openapi-templates/api.mustache
[main] INFO  o.o.codegen.TemplateManager - writing file openapi-templates/apiInner.mustache
[main] INFO  o.o.codegen.TemplateManager - writing file openapi-templates/model.mustache
[main] INFO  o.o.codegen.TemplateManager - writing file openapi-templates/README.mustache
...

Inspect what you got so you know which file controls what:

ls openapi-templates

3. Keep only the files you intend to override

This is the key step. Delete every extracted file except the one(s) you are changing. Here we keep only README.mustache and api.mustache:

cd openapi-templates
find . -type f ! -name 'README.mustache' ! -name 'api.mustache' -delete
ls

Expected output:

api.mustache  README.mustache

Now edit api.mustache to add a header. Mustache passes through anything that is not a {{tag}}, so a comment block goes straight to the top:

{{!-- openapi-templates/api.mustache --}}
/* Copyright (c) ACME Corp. Generated file — do not edit by hand. */
{{>licenseInfo}}
import globalAxios, { AxiosPromise, AxiosInstance, AxiosRequestConfig } from 'axios';

4. Generate with the template override

Point generate at the override directory with -t. The generator merges your two files over its defaults.

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

Confirm the header landed:

head -n 2 ./sdk/api.ts

Expected output:

/* Copyright (c) ACME Corp. Generated file — do not edit by hand. */
/* tslint:disable */

5. Protect hand-edited files with .openapi-generator-ignore

Sometimes you want to seed a file once (a package.json with extra scripts, a custom CI file) and then never let regeneration clobber it. The .openapi-generator-ignore file lives in the output directory and behaves like .gitignore.

cat > ./sdk/.openapi-generator-ignore <<'EOF'
# Protect hand-edited files from being overwritten on regeneration
package.json
.npmignore
git_push.sh
EOF

Re-run generate and confirm those files are skipped:

[main] INFO  o.o.codegen.TemplateManager - Skipped ./sdk/package.json (Ignored by rule in .openapi-generator-ignore.)

Complete Working Example

A single script that pins the generator, extracts defaults, trims to one override, and generates — reproducible from a clean checkout.

#!/usr/bin/env bash
# build-sdk.sh — customize one OpenAPI Generator template and generate a client.
# Requires: node 18+, npx. Pins openapi-generator-cli to 7.6.0.
set -euo pipefail

GEN=typescript-axios
TPL_DIR=openapi-templates
OUT_DIR=sdk
SPEC=openapi.yaml

# 1. Pin the generator version (deterministic builds in CI).
npx --yes @openapitools/openapi-generator-cli version-manager set 7.6.0

# 2. Extract built-in templates fresh each run, then trim to the overrides we own.
rm -rf "$TPL_DIR"
npx @openapitools/openapi-generator-cli author template -g "$GEN" -o "$TPL_DIR"
find "$TPL_DIR" -type f ! -name 'api.mustache' -delete

# 3. Add a license header to the kept template (idempotent).
HEADER='/* Copyright (c) ACME Corp. Generated file — do not edit by hand. */'
if ! grep -qF "$HEADER" "$TPL_DIR/api.mustache"; then
  printf '%s\n%s' "$HEADER" "$(cat "$TPL_DIR/api.mustache")" > "$TPL_DIR/api.mustache"
fi

# 4. Generate, applying the override directory.
npx @openapitools/openapi-generator-cli generate \
  -i "$SPEC" \
  -g "$GEN" \
  -t "$TPL_DIR" \
  -o "$OUT_DIR" \
  --additional-properties=npmName=@acme/api-client,supportsES6=true,withSeparateModelsAndApi=true

# 5. Verify the override took effect; fail the build if not.
head -n 1 "$OUT_DIR/api.ts" | grep -qF "$HEADER" \
  && echo "Template override applied." \
  || { echo "ERROR: override missing"; exit 1; }

Gotchas & Edge Cases

Template file names must match exactly. The override file name has to equal the built-in template name — api.mustache, not apis.mustache or Api.mustache. If your edits do nothing, the name or the -t path is wrong. Re-extract with author template and diff your file name against the real list.

Partials are separate files. Headers and reusable blocks are often partials referenced as {{>licenseInfo}}. If you override api.mustache but the change actually lives in a partial, edit licenseInfo.mustache instead — and keep that file in your override directory too, since a partial referenced but not provided still falls back to the default.

author template is per-generator. Templates for typescript-axios are not the templates for java or python. If you generate multiple languages, keep one override directory per generator; a model.mustache from one will not render correctly under another.

FAQ

How do I override only one OpenAPI Generator template file?

Extract the generator defaults with author template, keep only the single .mustache file you want to change in your template directory, and pass it back with -t. The generator falls back to its built-in templates for every file you did not provide.

Why are my template changes being ignored?

The most common cause is pointing -t at the wrong directory or naming the file incorrectly, since the override file name must match the built-in template name exactly. Run with --global-property debugModels and confirm the generator logs the path you expect.

What does the .openapi-generator-ignore file do?

It works like a .gitignore for generated output and prevents listed files from being overwritten on regeneration. Use it to protect hand-edited files such as README.md, package.json, or a custom build script.