Skip to content

OpenAPI Generator Customization — Extending Generated Code with Custom Templates and Logic

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you will learn about OpenAPI Generator Customization. We cover key concepts, practical examples, and best practices to help you master this topic.

OpenAPI Generator customization allows you to modify generated code with custom Mustache templates, post-generation scripts, custom generators, and custom libraries to match your project's conventions and requirements.

What You'll Learn

  • Custom Mustache template overrides
  • Post-generation hooks for code modification
  • Building custom generators for unsupported frameworks
  • Custom libraries for shared patterns
  • Template variables and context API

Why It Matters

Default generated code rarely matches your project's conventions. Customization ensures consistency, reduces manual cleanup, and adds project-specific features. DodaTech's generators produce code with custom logging, metrics, retry logic, and error handling — all through template overrides.

Real-World Use

A team generates Python SDKs but needs specific customizations: retry logic on every API call, structured logging with correlation IDs, and custom error types. Instead of editing generated code, they override the API client template to include these patterns automatically.

flowchart TD
    A["OpenAPI Spec"] --> B["Default Generator"]
    B --> C["Default Templates"]
    C --> D["Generated Code"]
    D -.->|"Manual edits lost on regeneration"| E["❌ Fragile"]

    A --> F["Custom Generator"]
    G["Custom Templates"] --> F
    H["Post-gen Hooks"] --> F
    I["Custom Library"] --> F
    F --> J["Custom Generated Code"]
    J --> K["✅ Regeneration-safe"]

Code Examples

Example 1: Custom Template Override

# Custom template: templates/python/api_client.mustache
# Overrides the default Python API client to add logging

import logging
logger = logging.getLogger(__name__)

class ApiClient:
    def __init__(self, configuration=None):
        self.configuration = configuration or Configuration()
        self.rest_client = RESTClientObject()

    def request(self, method, url, **kwargs):
        # Custom: add logging before request
        logger.info(f"API Request: {method} {url}")
        logger.debug(f"Headers: {kwargs.get('headers')}")

        response = self.rest_client.request(method, url, **kwargs)

        # Custom: add logging after response
        logger.info(f"API Response: {response.status}")
        logger.debug(f"Response body: {response.data[:500]}")

        if response.status >= 400:
            logger.error(f"API Error: {response.status} - {response.data}")

        return response

# Usage: override the default template
# openapi-generator-cli generate \
#   -i spec.yaml \
#   -g python \
#   -o /output \
#   -t /custom/templates/python/

Example 2: Post-Generation Hook Script

#!/usr/bin/env python3
"""
Post-generation hook: process generated code after generation.
"""
import os
import re
import shutil
from pathlib import Path

def add_retry_decorator(client_dir):
    """Add retry logic to all API client methods."""
    client_file = Path(client_dir) / 'api_client.py'
    content = client_file.read_text()

    # Add retry import
    if 'from tenacity' not in content:
        content = 'from tenacity import retry, stop_after_attempt, wait_exponential\n' + content

    # Add retry decorator to request method
    content = content.replace(
        'def request(self, method, url, **kwargs):',
        '@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=2, max=10))\n    def request(self, method, url, **kwargs):'
    )

    client_file.write_text(content)
    print("Added retry decorator to API client")

def add_correlation_ids(sdk_dir):
    """Add correlation ID header to all requests."""
    for py_file in Path(sdk_dir).rglob('*.py'):
        content = py_file.read_text()

        if 'correlation_id' in content:
            continue

        # Add correlation_id parameter to API methods
        content = re.sub(
            r'def (\w+)\(self,\s*',
            r'def \1(self, correlation_id=None, ',
            content
        )
        py_file.write_text(content)

    print("Added correlation ID support")

def remove_generated_logo(sdk_dir):
    """Remove generated logo files."""
    for logo in Path(sdk_dir).rglob('logo*'):
        logo.unlink()
        print(f"Removed {logo}")

# Run all hooks
add_retry_decorator('/tmp/sdks/python')
add_correlation_ids('/tmp/sdks/python')
remove_generated_logo('/tmp/sdks/python')

Example 3: Custom Generator for New Framework

from openapi_generator import DefaultGenerator
from openapi_generator.codegen import CodegenOperation, CodegenParameter

class CustomFastAPIGenerator(DefaultGenerator):
    """
    Custom generator for FastAPI-style Python server code.
    """
    def __init__(self):
        super().__init__()
        self.type_mappings = {
            'integer': 'int',
            'string': 'str',
            'boolean': 'bool',
            'number': 'float',
            'array': 'List',
            'object': 'Dict'
        }

    def generate_operation(self, operation: CodegenOperation) -> str:
        """Generate a FastAPI route handler."""
        method = operation.http_method.lower()
        path = operation.path

        params = []
        for param in operation.all_params:
            python_type = self.type_mappings.get(param.data_type, 'Any')
            if param.is_query:
                params.append(f"    {param.param_name}: {python_type} = Query(None)")
            elif param.is_path:
                params.append(f"    {param.param_name}: {python_type}")
            elif param.is_header:
                params.append(f"    {param.param_name}: {python_type} = Header(None)")

        param_str = '\n'.join(params)
        return f"""
@app.{method}('{path}')
async def {operation.operation_id}(
{param_str}
):
    \"\"\"
    {operation.summary or ''}

    {operation.description or ''}
    \"\"\"
    # TODO: Implement handler
    raise NotImplementedError()
"""

    def generate(self, spec):
        """Generate FastAPI server code from spec."""
        output = []
        output.append("from fastapi import FastAPI, Query, Header")
        output.append("from typing import List, Dict, Any, Optional")
        output.append("")
        output.append("app = FastAPI(title='Generated API', version='1.0.0')")
        output.append("")

        for path, methods in spec.get('paths', {}).items():
            for method, operation_data in methods.items():
                codegen_op = CodegenOperation(path, method, operation_data)
                output.append(self.generate_operation(codegen_op))

        return '\n'.join(output)

# Usage
generator = CustomFastAPIGenerator()
code = generator.generate(spec)

Common Mistakes

1. Editing Generated Code Directly

Generated code is ephemeral. Always customize through templates, hooks, or custom generators.

2. Overriding Too Many Templates

Override only the templates you need. Unnecessary overrides make upgrading the generator difficult.

3. Ignoring Template Context Variables

Templates have access to specific variables. Print all context variables before customizing a template.

4. Breaking Template Portability

Custom templates that hardcode framework-specific imports won't work across generator versions.

5. Not Testing Custom Generators

Custom generators need unit tests. Changes to the spec format can break custom generators silently.

Practice Questions

  1. How do you customize generated code without editing output?
  2. What is a post-generation hook?
  3. When should you build a custom generator?
  4. What are template context variables?
  5. How do you test custom templates?

Answers:

  1. Use custom Mustache templates (-t flag), post-generation hooks, or custom generators.
  2. A script that runs after generation to modify or augment the generated files automatically.
  3. When your target framework or language isn't supported, or when default generators don't match your conventions.
  4. Variables passed to Mustache templates containing codegen information (operations, models, parameters, etc.).
  5. Generate code with your custom templates, then run the generated code's test suite and verify the output structure.

Challenge: Create a custom template set for a Python generated SDK that adds structured logging, correlation ID support, and retry logic. Test that regeneration preserves all customizations.

FAQ

Where can I find the default templates?

: They're in the OpenAPI Generator JAR or installed package under modules/openapi-generator/src/main/resources/.

Can I share custom templates across projects?

: Yes. Store them in a Git Repository and reference the path in generation commands.

How do I debug template output?

: Add {{=<% %>=}} debug markers or use --enable-template-debug flag.

Are custom generators version-specific?

: Yes. Custom generators may need updates when OpenAPI Generator changes internally.

Can I mix default and custom templates?

: Yes. Override only specific templates; the rest fall back to defaults.

What's Next

Deepen your understanding with {{< ilink "OpenAPI" "Mustache Templates in OpenAPI Generator" }}, then build a {{< ilink "OpenAPI" "OpenAPI Generator Plugins" }} for reusable customizations.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro