Multiple configurations/specifying a configuration file
Description
Our projects needs clients/models for multiple different OpenAPI specifications.
Is it possible to specify multiple configurations in the config file, or to instruct the CLI to load a specific config file?
If this is already possible, some mention in the documentation would be helpful.
Hey, this isn't currently possible, see https://github.com/hey-api/openapi-ts/discussions/444. Are you able to share how you work around this today?
Currently working around this by not using a config file, and having multiple invocations with command line options in package.json.
I looked into this and the current blocker is that c12 does not support it. I'll see if I get a reply in the issue and if not, I will fork that package and add support. https://github.com/unjs/c12/issues/92
This is the workaround I'm using. Define multiple config values and switch on an env var.
import { defineConfig } from '@hey-api/openapi-ts';
const APIv1 = defineConfig({
input: 'https://myapi.com/v1/openapi.json',
output: 'api/v1',
client: '@hey-api/client-fetch',
});
const APIv2 = defineConfig({
input: 'https://myapi.com/v2/openapi.json',
output: 'api/v2',
client: '@hey-api/client-fetch',
});
let configExport;
switch (process.env.API_SPEC) {
case "APIv1": {
configExport = APIv1;
break;
}
case "APIv2": {
configExport = APIv2;
break;
}
default:
configExport = APIv1;
}
export default configExport;
Then commands can be run simply with:
API_SPEC=APIv1 npx @hey-api/openapi-ts
Our solution
openapi-generator.ts
import { execSync } from 'child_process';
import { Command } from 'commander';
import { existsSync, unlinkSync, writeFileSync } from 'fs';
type GenerateConfigOptions = {
input: string;
outputPath: string;
clientName: string;
httpClient: 'fetch' | 'axios';
enumType?: 'typescript' | 'javascript';
asClass: boolean;
};
function generateConfig(options: GenerateConfigOptions) {
const typesConfig = options.enumType
? `types: { enums: '${options.enumType}' },`
: '';
return `
import { defineConfig } from '@hey-api/openapi-ts';
export default defineConfig({
input: '${options.input}',
output: {
path: '${options.outputPath}',
format: 'prettier',
lint: 'eslint',
},
name: '${options.clientName}',
client: '${options.httpClient}',
${typesConfig}
services: {
asClass: ${options.asClass},
},
});
`;
}
const program = new Command();
program
.requiredOption(
'--input <urlOrPath>',
'Path to the input OpenAPI specification file',
)
.requiredOption('--client-name <string>', 'Name of the client to generate')
.requiredOption(
'--output-path <path>',
'Directory path where the generated client will be saved',
)
.requiredOption(
'--http-client <httpClient>',
'HTTP client to use (fetch or axios)',
(value) => {
if (!['fetch', 'axios'].includes(value)) {
throw new Error('Invalid HTTP client. Must be "fetch" or "axios".');
}
return value;
},
)
.requiredOption(
'--as-class <asClass>',
'Generate services as classes if true',
(value) => {
if (!['true', 'false'].includes(value)) {
throw new Error(
'Invalid value for asClass. Must be either true or false.',
);
}
return Boolean(value === 'true');
},
)
.option(
'--enum-type <enumType>',
'Type of enums to generate (typescript or javascript) (optional)',
(value) => {
if (value !== 'typescript' && value !== 'javascript') {
throw new Error(
'Invalid enum type. Must be either "typescript" or "javascript".',
);
}
return value;
},
);
program.parse(process.argv);
const options = program.opts() as GenerateConfigOptions;
const configFilePath = './openapi-ts.config.ts';
if (existsSync(configFilePath)) {
try {
unlinkSync(configFilePath);
console.log(`Existing file ${configFilePath} has been removed.`);
} catch (error) {}
}
const generatedConfig = generateConfig(options);
try {
writeFileSync(configFilePath, generatedConfig);
console.log(`Configuration file generated at ${configFilePath}`);
try {
execSync('yarn openapi-ts', { stdio: 'inherit' });
console.log('OpenAPI client generated successfully.');
} catch (error) {
console.error('Error generating OpenAPI client:', error);
unlinkSync(configFilePath);
process.exit(1);
}
unlinkSync(configFilePath);
} catch (error) {
console.error(`Error writing to file ${configFilePath}:`, error);
unlinkSync(configFilePath);
process.exit(1);
}
To use it:
yarn ts-node openapi-generator.ts \
--input $URL/api-json \
--client-name MyFancyClient \
--output-path src/my-fancy-client \
--http-client fetch \
--as-class true
If the openapi-ts cli was 1:1 with the config options in openapi-ts.config.ts, this issue won't be relevant anymore. So maybe the openapi-ts cli flags can be generated by the config? Maybe its worth looking into it instead of waiting for c12 to add multi config