Description
Bug Report Checklist
- [ x] Have you provided a full/minimal spec to reproduce the issue?
- [ x] Have you validated the input using an OpenAPI validator (example)?
- [ x] Have you tested with the latest master to confirm the issue still exists?
- [ x] Have you searched for related issues/PRs?
- [ x] What's the actual output vs expected output?
- [Optional] Sponsorship to speed up the bug fix or feature request (example)
Description
To me an API is a contract and stands alone without depending on any implementation. It is simply interface plus model classes depending on no implementation. Having an API depend on any implementation defeats the purpose of the definition of an API. When I generated api/model, it generates code assuming it would be for a client :( :( which is incorrect. We use the API on our server and our client. more below.
Expected an API (one interface + multiple DTO model classes) that compiles when using "--global-property models,apis" such that on client, I can have my client implement it. On server, I can have my controller implement it. Instead generated classes do not compile and refer to a non-existent ../runtime for many many things which makes it hard to use the generated content both on server and client (we generate into a lib for our monorepo that both server and clients use so they 100% use the same code. This code is then implemented by controller in server or by a semi-generated client in the client side. The generated code for client and server shoudl contain 100% the same api and 100% the same model classes.
openapi-generator version
not sure, but CLI version ->
(.venv) 09:15 ~/workspace/biltup/biltup/scripts $ openapi-generator-cli version
Did set selected version to 7.12.0
7.12.0
OpenAPI declaration file content or url
openapi: 3.0.3
info:
title: Database API
version: "1.0.0"
paths:
/fetchData:
post:
summary: Fake Endpoint
operationId: /fetchData
requestBody:
description: Request payload to fetch customer details.
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/FetchDataRequest'
responses:
'200':
description: Successful response with customer details.
content:
application/json:
schema:
$ref: '#/components/schemas/FetchDataResponse'
components:
schemas:
FetchDataRequest:
type: object
description: Empty request payload for fetching customer details.
properties:
placeHolder:
type: string
description: Placeholder
FetchDataResponse:
type: object
description: Response containing the customer details.
properties:
placeHolder:
type: string
description: Placeholder
Generation Details
Doing this for BOTH server and client (however neither compile)
openapi-generator-cli generate
-i biltup-db-ai.api.yaml
-g typescript-fetch
-o ${SERVICES_DIR}/biltup-db/src/apis-gen/server
--global-property models,apis
--additional-properties=withInterfaces=true,supportsES6=true,typescriptThreePlus=true
Steps to reproduce
Run the command and it does not compile. missing "../runtime" and "../model/index".
Expected results: It contains only the 'API contract' and compiles. APIs should compile by themselves. In this example, if you really need RequestInit as part of contract, generate RequestInit but do not generate the client. I would much prefer simply
export interface DefaultApiInterface {
/**
*
* @summary Fetch Customer Details
* @param {object} body Request payload to fetch customer details.
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof DefaultApiInterface
*/
fetchCustomerRaw(requestParameters: FetchCustomerRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<FetchCustomerResponse>>;
/**
* Fetch Customer Details
*/
fetchCustomer(requestParameters: FetchCustomerRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<FetchCustomerResponse>;
}
Personally,I would much prefer you to generate this as you can set all initOverrides in asyncLocalStorage and errors, we use underlying json ProtocolError when received by client, we throw error on client. Server receives Error and translates to ProtocolError. In this way, we never need your ApiResponse that you ahve here as exceptions are always Errors. All our apis currently without open api that are shared between typescript projects are of below format containing no extra code. We implement with controller and on client implement with a client ...
Now this is code I can use on both the client and the server AND EVEN BETTER, for testing, we embed a whole microservice in another since the APIs are 100% the same. We pass traceId, login, email, companyId, etc. etc using asynLocalStorage + http headers so we get full tracing as well without corrupting the API with this RequestInit object and can further configure other things if we want with asyncLocalStorage. Having the apis be 100% the same is extremely useful. This is just super clean ->
//DefaultApiInterface should really be called from the title of my open api spec as this will clash with other apis too
//We have microservices that implement 2 apis sometimes as makes it easy to transition over or in preparation for separation later as well
export interface DefaultApiInterface {
/**
* Fetch Customer Details
*/
fetchCustomer(requestParameters: FetchCustomerRequest): Promise<FetchCustomerResponse>;
}
Related issues/PRs
Suggest a fix
Perhaps this is a feature request(I was not sure but to me api, model generation should compile. I have never seen an api be tied to implementation. that defeats the purpose of an api to be a contract).