openapi-typescript-codegen: (FormData) TypeError: source.on is not a function
What is the problem?
I’m using openapi-typescript-codegen
for creating an axios
client. When I try to call one of the services I end up getting the following error:
.../_nestjs/node_modules/delayed-stream/lib/delayed_stream.js:33
source.on('error', function() {});
^
TypeError: source.on is not a function
at Function.DelayedStream.create (.../_nestjs/node_modules/delayed-stream/lib/delayed_stream.js:33:10)
at FormData.CombinedStream.append (.../_nestjs/node_modules/combined-stream/lib/combined_stream.js:45:37)
at FormData.append (.../_nestjs/node_modules/form-data/lib/form_data.js:75:3)
at process (.../_nestjs/client/app/src/generated/core/request.ts:117:26)
at .../_nestjs/client/app/src/generated/core/request.ts:127:40
at Array.forEach (<anonymous>)
at .../_nestjs/client/app/src/generated/core/request.ts:127:27
at Array.forEach (<anonymous>)
at getFormData (.../_nestjs/client/app/src/generated/core/request.ts:125:14)
at .../_nestjs/client/app/src/generated/core/request.ts:294:41
Like the stacktrace shows the error is thrown inside request.ts
file in the getFormData
function.
export const getFormData = (options: ApiRequestOptions): FormData | undefined => {
if (options.formData) {
const formData = new FormData();
const process = (key: string, value: any) => {
if (isString(value) || isBlob(value)) {
formData.append(key, value); // 🚨 this is where it breaks (isBlob == true)
} else {
formData.append(key, JSON.stringify(value));
}
};
Object.entries(options.formData)
.filter(([_, value]) => isDefined(value))
.forEach(([key, value]) => {
if (Array.isArray(value)) {
value.forEach(v => process(key, v));
} else {
process(key, value);
}
});
return formData;
}
return undefined;
};
Reproducable example
❯ node -v
v20.10.0
package.json
{
"name": "api",
"version": "0.0.1",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"files": [
"dist"
],
"scripts": {
"prebuild": "rm -rf dist",
"gen": "openapi -i openapi.json -o src/generated -c axios --useOptions --useUnionTypes",
"build": "tsc"
},
"dependencies": {
"axios": "^1.6.2",
"form-data": "^4.0.0"
},
"devDependencies": {
"@types/node": "^20.10.4",
"openapi-typescript-codegen": "0.25.0",
"ts-node": "^10.9.2",
"tsc": "2.0.4",
"typescript": "^5.3.3"
}
}
tsconfig.json
{
"compilerOptions": {
"experimentalDecorators": true,
"declaration": true,
"removeComments": false,
"emitDecoratorMetadata": true,
"allowSyntheticDefaultImports": true,
"incremental": false,
"strictNullChecks": false,
"noImplicitAny": false,
"strictBindCallApply": false,
"noFallthroughCasesInSwitch": false,
"lib": ["ES2022", "dom"],
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": false,
"skipLibCheck": true,
"baseUrl": "./",
"rootDir": "./",
"outDir": "dist"
},
"include": ["src"],
"exclude": ["node_modules"]
}
My openapi
spec is looking like this:
{
"openapi": "3.0.0",
"paths": {
"/mail/send": {
"post": {
"operationId": "sendMail",
"summary": "Send a generic mail",
"parameters": [
{ "name": "subject", "required": true, "in": "query", "schema": { "type": "string" } },
{ "name": "recipients", "required": true, "in": "query", "schema": { "type": "array", "items": { "type": "string" } } },
{ "name": "displayName", "required": true, "in": "query", "schema": { "type": "string" } },
{ "name": "replyTo", "required": true, "in": "query", "schema": { "type": "string" } },
{ "name": "ccs", "required": false, "in": "query", "schema": { "type": "array", "items": { "type": "string" } } },
{ "name": "bccs", "required": false, "in": "query", "schema": { "type": "array", "items": { "type": "string" } } }
],
"requestBody": { "required": true, "content": { "multipart/form-data": { "schema": { "$ref": "#/components/schemas/SendGenericMailBodyDto" } } } },
"responses": { "201": { "description": "" } },
"tags": ["Mail"],
"security": [{ "JWT": [] }]
}
}
},
"info": { "title": "API" },
"tags": [],
"servers": [],
"components": {
"schemas": {
"SendGenericMailBodyDto": { "type": "object", "properties": { "attachments": { "type": "array", "items": { "type": "string", "format": "binary" } } }, "required": ["attachments"] }
}
}
}
The generated code for that service looks like this:
...
export class MailService {
/**
* Send a generic mail
* @returns any
* @throws ApiError
*/
public static sendMail({
subject,
recipients,
displayName,
replyTo,
formData,
ccs,
bccs,
}: {
subject: string,
recipients: Array<string>,
displayName: string,
replyTo: string,
formData: SendGenericMailBodyDto,
ccs?: Array<string>,
bccs?: Array<string>,
}): CancelablePromise<any> {
return __request(OpenAPI, {
method: 'POST',
url: '/mail/send',
query: {
'subject': subject,
'recipients': recipients,
'displayName': displayName,
'replyTo': replyTo,
'ccs': ccs,
'bccs': bccs,
},
formData: formData,
mediaType: 'multipart/form-data',
});
}
}
Now when I try to call that service the error occurs:
import * as MAIL from '..';
export async function sendMail() {
const obj = { hello: 'world' };
const blob = new Blob([JSON.stringify(obj, null, 2)], {
type: 'application/json',
});
await MAIL.MailService.sendMail({
displayName: 'Test',
recipients: ['test@test.com'],
subject: 'Test',
replyTo: 'test@test.com',
formData: {
attachments: [blob],
},
});
}
Am I missing something?
About this issue
- Original URL
- State: open
- Created 7 months ago
- Reactions: 2
- Comments: 18
Check out our fork of this repository @hey-api/openapi-ts. We have fixed this issue in
v0.32.1
. If you run into any further issues, open an issue in our repository. Thanks.NOTE: this is now fixed for both
node
andaxios
clients. No other clients experience this issue.@costa-collibra @mrlubos it has been fixed in the node client, working on updating the axios client to be fixed aswell. Should be in the next release
Oh I didn’t know about that fork / project 😱 Will check it out 🤝 Thanks for the hint! @jordanshatford @costa-collibra @mrlubos
I have also had this exact same issue when trying to use the generated code to upload a file. For now I have had to build my own request using axios. I am using the client in my e2e Playwright tests for creating test data.
I can provide more context if and examples if you are looking into this issue.
Thanks