This guide explains how to add support for a new programming language to @eulerstream/openapi-generator-examples.
The package generates per-operation usage example .md files from an OpenAPI spec. Each language needs:
- A Language Adapter — TypeScript class implementing
LanguageAdapterthat handles language-specific naming, typing, and code generation - A Mustache Template — Default template that renders a complete code example
- Registration — Adding the adapter to
src/languages/register-all.ts
OpenAPI Spec → Spec Parser → Normalized Operations
↓
Config (YAML) + Language Adapter → Template Context
↓
Mustache Template → Rendered Example → .md files
The spec parser normalizes the OpenAPI spec into NormalizedOperation[]. The template context builder combines operations with config + language adapter to produce a TemplateContext. The Mustache renderer renders the template with the context to produce the example code string.
Create src/languages/<language>.ts. Your adapter must implement the LanguageAdapter interface:
import { registerLanguage } from './registry.js';
import type { LanguageAdapter, MethodCallOptions } from './types.js';
import type { NormalizedParam, NormalizedRequestBody, NormalizedSchema } from '../spec/types.js';
const adapter: LanguageAdapter = {
id: 'mylang',
generatorNames: ['mylang', 'mylang-client'],
codeBlockLang: 'mylang',
toMethodName(operationId: string): string {
// Convert operationId to your language's method naming convention
// e.g., camelCase for Java/TS, snake_case for Python, PascalCase for Go/C#
},
toFileName(operationId: string): string {
// Convert operationId to a file-system-safe name
},
toTagDirectory(tag: string): string {
// Convert an API tag to a directory name
},
mapType(schema: NormalizedSchema): string {
// Map OpenAPI types to your language's native types
// Handle: string, integer, number, boolean, array, object, enums
},
exampleValue(param: NormalizedParam): string {
// Generate an example value for a parameter
// Use param.example, param.schema.enum, param.schema.default, or synthesize
},
buildParamDeclaration(param: NormalizedParam): string {
// Generate a variable declaration with example value
// e.g., `String petId = "pet_123";`
},
buildMethodCall(opts: MethodCallOptions): string {
// Build the method invocation expression
// Use opts.apiAccessPattern: "dot" → client.pets.list()
// "call" → client.pets().list()
},
buildBodyConstruction(body: NormalizedRequestBody): string {
// Generate request body object construction
},
buildResultLine(call: string, returnType: string | undefined): string {
// Generate the result assignment line
// e.g., `MyType result = call;`
},
};
// Self-register on import
registerLanguage(adapter);
export default adapter;Convert the OpenAPI operationId to your language's method naming convention:
- TypeScript/Java:
camelCase→listPets - Python:
snake_case→list_pets - Go/C#:
PascalCase→ListPets
Map OpenAPI schema types to native types. Handle these cases:
string→String,str,stringinteger→int,Integer,int64number→double,float,float64boolean→bool,Booleanarraywithitems→List<T>,[]T,list[T]object→ named type or generic mapenum→ language-appropriate enum reference
Generate realistic example values. Priority:
- Use
param.exampleif present in the spec - Use
param.schema.enum[0]for enum params - Use
param.schema.defaultif present - Synthesize based on type + name heuristics (e.g., param named
email→"user@example.com")
Generate code to construct the request body. If body.schemaName is present, use the named type. Use body.schema.properties to set fields with example values.
Create src/templates/defaults/<language>.mustache. The template receives a TemplateContext object.
{{operationId}} - Operation ID from spec
{{methodName}} - Language-appropriate method name
{{tag}} - API tag
{{httpMethod}} - HTTP method (GET, POST, etc.)
{{path}} - URL path
{{description}} - Operation description
{{apiProperty}} - Resolved wrapper property name
{{#hasParams}}...{{/hasParams}} - Has any parameters
{{#hasRequiredParams}}...{{/hasRequiredParams}} - Has required params
{{paramDeclarations}} - Pre-rendered param declarations
{{methodCall}} - Pre-rendered method call expression
{{resultLine}} - Pre-rendered result assignment line
{{#hasBody}}...{{/hasBody}} - Has request body
{{bodyTypeName}} - Body schema name
{{bodyConstruction}} - Pre-rendered body construction code
{{#boilerplate.showImports}}...{{/boilerplate.showImports}}
{{#boilerplate.showTryCatch}}...{{/boilerplate.showTryCatch}}
{{#boilerplate.showApiKeyConfig}}...{{/boilerplate.showApiKeyConfig}}
{{#variables}}
{{sdkImport}} - Import statement(s)
{{clientConstruction}} - Client construction code
{{clientVar}} - Client variable name
{{apiKeyPlaceholder}} - API key placeholder string
{{apiAccessPattern}} - "dot" or "call"
{{/variables}}
{{#boilerplate.showImports}}
{{{variables.sdkImport}}}
{{/boilerplate.showImports}}
{{{variables.clientConstruction}}}
{{#hasRequiredParams}}
{{{paramDeclarations}}}
{{/hasRequiredParams}}
{{#hasBody}}
{{{bodyConstruction}}}
{{/hasBody}}
{{{resultLine}}}Note the use of {{{triple-braces}}} for unescaped output (we disable HTML escaping globally, but triple braces are conventional for multi-line content).
Add your import to src/languages/register-all.ts:
import './mylang.js';Users configure per-language behavior via config.yml:
# examples.config.yml for MyLang SDK
boilerplate:
showTryCatch: false
showImports: true
showApiKeyConfig: false
variables:
sdkImport: "import MySDK from 'my-sdk'"
clientConstruction: |
client = MySDK.new(api_key: "YOUR_API_KEY")
clientVar: client
apiKeyPlaceholder: YOUR_API_KEY
apiAccessPattern: dot
apiClassMap:
Pets: pets
Store: store
# Optional: override the default template
# templatePath: ./my-custom-template.mustache| Field | Description |
|---|---|
boilerplate.showTryCatch |
Wrap examples in try-catch blocks |
boilerplate.showImports |
Show import statements |
boilerplate.showApiKeyConfig |
Show API key setup |
boilerplate.showFullClass |
Show full class wrapper (e.g., Java public class Example { main... }) |
variables.sdkImport |
The import statement(s) for your SDK |
variables.clientConstruction |
Code to construct the client |
variables.clientVar |
Variable name for the client |
variables.apiAccessPattern |
"dot" for client.api.method(), "call" for client.api().method() |
apiClassMap |
Maps OpenAPI tags to wrapper property names |
templatePath |
Path to custom mustache template (overrides built-in) |
If the default template doesn't meet your needs, provide a custom template:
templatePath: ./my-template.mustacheYour custom template receives the exact same TemplateContext object as the default template. You can use any combination of the available variables, add custom formatting, or include additional fixed content.
Generated files are organized by language and API tag:
usage/
mylang/
pets/
listPets.md
createPet.md
getPetById.md
store/
getInventory.md
index.md
Each .md file contains:
- Operation title and description
- HTTP method and path
- Code example (rendered from the template)
- Parameter table
- Request body info (if applicable)