Generating code based on multiple OpenAPI schemas
Description
Hey everyone!
I am facing a challenge using the library. On the project I am working on, we have several microservices, each of them with its own OpenAPI schema, which means that I have to deal with different .yaml files.
I am not sure if this is possible, but is there any way to declare several inputs instead of just one? kind of:
import { defineConfig } from '@hey-api/openapi-ts'
export default defineConfig({
client: '@hey-api/client-axios',
input: {
"service1": '@generated/openapi-schemas/service1/service1.yaml',
"service2": '@generated/openapi-schemas/service2/service2.yaml',
...
},
output: {
format: 'prettier',
lint: 'eslint',
path: '@generated/api',
},
})
I was checking the ClientConfig documentation, and it looks like the input property can be an object, but I am doing something wrong because it's not working as I expected.
I would appreciate any guidance.
Thanks a lot in advance!
Hey @frankPairs, it can be an object, but it does not support what you're describing. This should be easy to add though. What's up with the "service1" naming? What's the expected output?
My first intuition is that the files generated from @generated/openapi-schemas/service1/service1.yaml would be within the output folder using the same name. Just following the example describe above, it would look like:
I'd like to do something similar, but was thinking something like this would work, and found out it doesn't:
export default [
defineConfig({
client: '@hey-api/client-fetch',
input: 'https://api1/openapi.json',
output: 'src/clients/api1',
}),
defineConfig({
client: '@hey-api/client-fetch',
input: 'https://api2/openapi.json',
output: 'src/clients/api2',
}),
];
Like said by @frankPairs, i will be very interested by this functionnality because on my project i have several swagger, and i would wish they are classified by input name in output directory. But actually, I have this issue if i launch the command
npm run openapi
Unexpected error occurred. Unsupported OpenAPI specification: {
while in the documentation, the input type is a string or a Record<string, unknown>;
On project openapi-typescript-codegen
https://github.com/ferdikoomen/openapi-typescript-codegen/tree/main?tab=readme-ov-file
I was not bothered because I was running X commands (in the package.json) for X swagger to get the data generated.
openapi -i http://localhost:8080/api/xxxx/swagger.json -o src/api/xxxx --client axios --indent 2
We also have multiple services we generate clients for. We simply have a base openapi-ts/defineConfig.ts, which prefixes all methods through methodeNameBuilder:
import { defineConfig as openapiTsDefineConfig } from '@hey-api/openapi-ts';
export const defineConfig = ({ input, name }: { input: string; name: string }) =>
openapiTsDefineConfig({
client: '@hey-api/client-fetch',
experimentalParser: true,
input: input,
output: {
path: `app/modules/api/${name}`,
format: 'prettier',
lint: 'eslint',
},
plugins: [
{
name: '@hey-api/types',
enums: 'javascript',
},
{
name: '@hey-api/services',
methodNameBuilder: (operation) => {
const id = operation.id;
if (!id) {
throw new Error('Missing operation.id');
}
return `${name}${id.charAt(0).toUpperCase() + id.slice(1)}`;
},
},
'@tanstack/react-query',
],
});
Then we have separate config file for each service, openapi-ts/management.config.ts:
import { defineConfig } from './config';
export default defineConfig({
input: 'http://url-to-management-spec.openapi',
name: 'management',
});
And openapi-ts/analysis.config.ts:
import { defineConfig } from './config';
export default defineConfig({
input: 'http://url-to-analysis-spec.openapi',
name: 'analysis',
});
Then we run them like:
openapi-ts --file openapi-ts/management.config.ts && openapi-ts --file openapi-ts/analysis.config.ts
Probably could be a bit smoother by calling them from node or similar, but it works.
I made script
import { createClient, defaultPlugins } from '@hey-api/openapi-ts';
const services = {
one: 'https://service.one-json',
two: 'https://service.two-json',
three: 'https://service.three-json',
};
Object.entries(services).forEach(([serviceName, url]) => {
createClient({
client: '@hey-api/client-fetch',
input: url,
output: {
path: `src/http-client/types/${serviceName}`,
format: 'prettier',
lint: 'eslint',
indexFile: false,
},
plugins: defaultPlugins.filter((plugin) => plugin !== '@hey-api/sdk'),
});
});
then just call script and will generate multiple types
This is an essential feature imo. The advantage of OpenAPI is to be able to pull in any api you want and easily use as client in your code base. Having openapi-ts support only one client by default works if you have one backend and a frontend but breaks down as soon as you'd like to use micro services or any combination of openapi apis on your backend as well.
any update on this?
any update on this?
https://github.com/hey-api/openapi-ts/issues/1056#issuecomment-2483291269 is working quite well for me, and sadly this is the reason why I didn't put more energy by contributing to it 😅 You should try that workaround, or you could think about to contribute and open a PR If you like you can call me to review your PR
Hey all, @carson2222 will be looking at this feature, he'll be your point of contact for getting this over the finish line 🐎
I'm currently handling the multi-config feature. I added support for an array of objects to defineConfig function. Would this cover all your needs? Now i'm probably going to move on to allowing multiple inputs to one config
import { defineConfig } from "@hey-api/openapi-ts";
export default defineConfig([
{
input: "./openapi1.yaml",
output: "src/client1",
},
{
input: "./openapi1.yaml",
output: "src/client2",
},
{
input: "./openapi2.yaml",
output: "src/client3",
},
]);
hey @carson2222 ! I think your proposal looks nice to solve the problem that I described on this issue.
I'm glad you like it. This and some more config features will be added soon once the https://github.com/hey-api/openapi-ts/pull/2602 gets merged 🫡
Hey @carson2222 any idea on when this will get merged? Would love to use this!
@pachecoka can you try the preview in https://github.com/hey-api/openapi-ts/pull/2602#issuecomment-3281090710 and let us know if it works as expected? I wanted to clean up the internals a bit before merging, functionally it should just work already
Hey @mrlubos,
I tried it out and it works.
Only thing I noticed is that now in the generated code for client/client.gen.ts there is a function called makeSseFn and it makes use of BodyInit. This type was not being found and giving me an error, but adding the following to tsconfig made it work.
"lib": ["ESNext", "DOM"]
This will be available in the next release. Looking forward to your feedback!
Thanks for this feature, it's working. That's my adopted example, as prefixing the methods might still be necessary, but now in a single openapi-ts.config.ts:
import { defineConfig } from '@hey-api/openapi-ts';
export default defineConfig(
[
{
input: 'https://data-service.localhost/api/docs/openapi',
name: 'data',
},
{
input: 'https://management-service.localhost/api/docs/openapi',
name: 'management',
},
].map(({ input, name }: { input: string; name: string }) => {
const methodName = (id: string) => `${name}Api${id.charAt(0).toUpperCase() + id.slice(1)}`;
return {
input: input,
output: {
path: `src/modules/api/${name}`,
lint: 'eslint',
},
parser: {
hooks: {
operations: {
isQuery: (operation) => {
if (operation.method === 'post') {
return true;
}
},
},
},
},
plugins: [
'@hey-api/client-fetch',
{
name: '@hey-api/typescript',
enums: {
mode: 'javascript',
case: 'preserve',
},
},
{
name: '@hey-api/sdk',
methodNameBuilder: (operation) => {
const id = operation.id;
if (!id) {
throw new Error('Missing operation.id');
}
return methodName(id);
},
},
{
name: '@tanstack/react-query',
queryOptions: {
name: (id) => {
return `${methodName(id)}Options`;
},
},
queryKeys: {
name: (id) => {
return `${methodName(id)}QueryKey`;
},
},
mutationOptions: {
name: (id) => {
return `${methodName(id)}Mutation`;
},
},
},
'zod',
],
};
}),
);
We run this with openapi-ts && prettier --write './src/modules/api/**/*.ts' --log-level error due to #2200. Also looking forward to #2579.
@KiwiKilian this is starting to look clean!