OpenAPITools / OpenAPITools/openapi-generator
Generated Node Express Server Wont Handle mutlipart/form-data
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 26.8k
- Forks
- 7.7k
- PR merge metrics
- PR metrics pending
Description
Though I've used the generator for several API's this is the first time I've tried adding a route to support file upload.
I've effectively followed this documented process:
https://swagger.io/docs/specification/describing-request-body/multipart-requests/
However I have tried several variants, none of which have worked.
The path:
"/upload_video":{
"post": {
"description": "Upload a video or series of videos ",
"operationId": "uploadFiles",
"requestBody": {
"description": "Videos to upload",
"content": {
"multipart/form-data": {
"schema": {
"$ref": "#/components/schemas/UploadVideo"
}
}
}
},
"responses": {
"200": {
"description": "successful operation",
"content": { }
},
"400": {
"description": "Invalid bucket",
"content": {}
},
"403": {
"description": "Forbidden directory is not writeable or file exists but not overwritten ",
"content": {}
}
},
"deprecated": false,
"security": [
{
"api_key": []
}
],
"summary": "Upload video to provided video_category.",
"tags": [ "video_category" ]
}
},
The referenced schema component:

I've tried with a schema using a reference (as shown above) and also inline (rather than use a reference).
I've generated both a node express service stub using:
npx ./node_modules/.bin/openapi-generator-cli generate -i InterfaceSpecification.json -g nodejs-express-server -o test_srvr_stubs
and a javascript client:
npx ./node_modules/.bin/openapi-generator-cli generate -i InterfaceSpecification.json -g javascript -o test_lib_javascript
The client library is used in a react application, and it can generate the request to the service..
However the service returns a 500 code.

I've dug into the service and found where it is crashing.
The error can be seen in the following print of the error:
ddd TypeError: Cannot convert undefined or null to object
at Function.keys (<anonymous>)
at Function.collectRequestParams (/home/dsargrad/dev/vm_api_svc/srvr/controllers/Controller.js:86:16)
at Function.handleRequest (/home/dsargrad/dev/vm_api_svc/srvr/controllers/Controller.js:114:59)
at uploadFiles (/home/dsargrad/dev/vm_api_svc/srvr/controllers/VideoCategoryController.js:25:20)
at Layer.handle [as handle_request] (/home/dsargrad/dev/vm_api_svc/srvr/node_modules/express/lib/router/layer.js:95:5)
at next (/home/dsargrad/dev/vm_api_svc/srvr/node_modules/express/lib/router/route.js:144:13)
at Route.dispatch (/home/dsargrad/dev/vm_api_svc/srvr/node_modules/express/lib/router/route.js:114:3)
at Layer.handle [as handle_request] (/home/dsargrad/dev/vm_api_svc/srvr/node_modules/express/lib/router/layer.js:95:5)
at /home/dsargrad/dev/vm_api_svc/srvr/node_modules/express/lib/router/index.js:284:15
at Function.process_params (/home/dsargrad/dev/vm_api_svc/srvr/node_modules/express/lib/router/index.js:346:12)
The generated code includes controllers/Controller.js
That file includes the following static method:
static collectRequestParams(request) {
const requestParams = {};
if (request.openapi.schema.requestBody !== undefined) {
const { content } = request.openapi.schema.requestBody;
if (content['application/json'] !== undefined) {
const requestBodyName = camelCase(this.getRequestBodyName(request));
requestParams[requestBodyName] = request.body;
} else if (content['multipart/form-data'] !== undefined) {
Object.keys(content['multipart/form-data'].schema.properties).forEach(
(property) => {
const propertyObject = content['multipart/form-data'].schema.properties[property];
if (propertyObject.format !== undefined && propertyObject.format === 'binary') {
requestParams[property] = this.collectFile(request, property);
} else {
requestParams[property] = request.body[property];
}
},
);
}
}
request.openapi.schema.parameters.forEach((param) => {
if (param.in === 'path') {
requestParams[param.name] = request.openapi.pathParams[param.name];
} else if (param.in === 'query') {
requestParams[param.name] = request.query[param.name];
} else if (param.in === 'header') {
requestParams[param.name] = request.headers[param.name];
}
});
return requestParams;
}
Notice that in the else if for multipart/form-data, it performs a forEach on
Object.keys(content['multipart/form-data'].schema.properties).forEach(
However the referenced "schema.properties" is undefined:

The schema includes a reference to the model:

This is true even if the initial model parameters was included inline (the generator generates openapi.yaml, and automatically creates a model and then references it).
I've struggled with this for a day, and would appreciate guidance.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the generated controllers/Controller.js collectRequestParams method and reproduce the multipart/form-data request using the referenced UploadVideo schema. Compare how the generated schema is represented in openapi.yaml with the properties access in Controller.js. Done means the generated Node Express server handles the referenced multipart schema without the TypeError and collects the upload fields.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- express, javascript, nodejs, openapi
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 30/100