Tsoa: How to set a file as parameter or response?

Created on 28 Apr 2017  Â·  22Comments  Â·  Source: lukeautry/tsoa

How to write TSOA code that uses files as input parameter and response? I could not find any rerference that hints that TSOA is supporting the file datatype of the swagger definition. Is it missing? Will it be added in the future?

I try to recreate an existing swagger definition with TSOA and stumbled over a missing file keyword(?).
My swagger definition is like that:

/system/update/firmware:
post:
tags:
- System
- Update
summary: update system firmware with new firmware image
description: update system firmware with new firmware image
operationId: updateFirmware
parameters:
- in: formData
name: file
description: uploaded firmware image file
required: true
type: file
responses:
"200":
description: successfull operation
"400":
description: invalid firmware image provided
security:
- project_auth:
- system_install
/system/export:
post:
tags:
- System
- Backup
description: export system preferences and configuration to external file system
operationId: exportSystemBackup
responses:
"200":
description: export OK
schema:
type: file
description: file that contains the backup of system preferences and settings
"400":
description: export failure
security:
- project_auth:
- system_export

As you can see there is a file as input parameter and return value. The swagger.io definition supports files as own data type. I try to generate a similiar definition by generating Typescript code and adding TSOA anotations.

When generating TypeScript client code and modifying it to server side code I get this:

>

...
@Post('export')
public async exportSystemBackup ( ) : Promise {
....
@Post('update/firmware')
public async updateFirmware (@Body() file: any ) : Promise {
...

But when generating routes I get following error message:

tsoa swagger

Error: Unknown type: AnyKeyword
at ResolveType (.../resolveType.js:30:15)

I looked in the TSOA repository but could not find the any or file keyword handled in any way. I could not find an example for my use case either. So could someone help? Thank you.

Stale enhancement

Most helpful comment

Swagger has specifications for "multipart/form-data" https://swagger.io/specification/#parameterObject

That is - in terms of HTML Forms - a form based file uploads.

Express.JS comes with a middle ware for this:
https://github.com/expressjs/multer

Multer needs some configuration and injection into the route.ts file. I think this is possible with the same ideas as we have them in the authorizsation and the IOC.

So pointing to the swagger docs I suggest the following new Parameter:

// The Client has to send us Content-Disposition: form-data; name="parameterName"
@Form('multipart/form-data', parameterName) parameterName

// The Client has to send us in the path "?parameterName=1" - this is like a query parameter
// and basically has nothing to do with a file upload at all!
@Form('application/x-www-form-urlencoded', parameterName) parameterName

// this defaults to the "multipart/form-data"
@Form() parameterName

All 22 comments

+1 This is preventing me from using tsoa. Though I'm not exactly sure as to how to solve the issue. Maybe using an @Upload decorator?
EDIT
On second thought, the solution to support the file type won't work with just an @Upload decorator :/

Some ideas...
routing-controllers uses an @UploadedFile(s) decorators to inject a file into the parameter of the controller
https://github.com/pleerock/routing-controllers#inject-uploaded-files
The only issue(?) is that it's using multer as a dependency to support files.
Also, when generating routes, it could look for the metadata generated by the @UploadedFile(s) decorators to know whether it should generate a route that supports file uploads. And when generating the swagger docs, the @UploadedFile(s) decorators would indicate when there is a parameter of type "file" required for a specific route.

While this would solve files as input parameters, I'm not exactly sure how it would be supported in the response. Any thoughts?

I think a solution could be easier. TSOA already evaluates the used classes and types and transforms them to types that TSOA can handle. The class File should be added to that types and the generator should generate a proper swagger definition using the swagger "file" keyword.

I'm new to TypeScript and node.js services. So I'm not sure how a proper API that use file upload and download should look like. I tried some API variants replacing the "File" result and parameter with "Buffer" and "Blob". TSOA can generate routes and the API with this types but handles them as string (in the swagger definition). File parameter do not work because the class contains "any" member that can't be processed by TSOA.

I don't think tsoa currently generates routes that correctly support file uploads when using the command

tsoa routes

It might generate some swagger definitions that have the parameter with the name "file" but with the incorrect type.

In order to fully support a file as a parameter, I believe tsoa should generate the correct routes (in routes.ts) with support for file uploads (which can be achieved in this case with multer) as well as the correct swagger definition with the type "file". The Express.Multer.File type could also be leveraged instead of creating a new class.

Again, this doesn't solve file as a return type, but I'm guessing that the express.static middleware can be used (I'm sure hapi and koa have similar middleware) when generating the routes.ts.

Right now, File uploads isn't supported from a swagger OR a route standpoint. Ideally, it would be supported for both.

File returns would be...weird. File posts should be more straightforward, akin to the support for base64/binary.

I will try to get something together relatively soon, but PRs are always welcome if someone would like to take a stab at it.

I'd be happy to take a stab at it @lukeautry

Swagger has specifications for "multipart/form-data" https://swagger.io/specification/#parameterObject

That is - in terms of HTML Forms - a form based file uploads.

Express.JS comes with a middle ware for this:
https://github.com/expressjs/multer

Multer needs some configuration and injection into the route.ts file. I think this is possible with the same ideas as we have them in the authorizsation and the IOC.

So pointing to the swagger docs I suggest the following new Parameter:

// The Client has to send us Content-Disposition: form-data; name="parameterName"
@Form('multipart/form-data', parameterName) parameterName

// The Client has to send us in the path "?parameterName=1" - this is like a query parameter
// and basically has nothing to do with a file upload at all!
@Form('application/x-www-form-urlencoded', parameterName) parameterName

// this defaults to the "multipart/form-data"
@Form() parameterName

Hello,
How can we help to make this annotation work ?
Any news about merge into master ?

I am lazy :)))) - Or busy.

I need this very very soon - but I don't have the time to fix it (yet).

My idea (well at last for express.js) is to multer as middleware
https://www.npmjs.com/package/multer

I currently create a manual route and create an instance of the controller out of the controll of router.ts

You can go with something like this (not currenty supported by tsoa) but at last you have a working solution until I am not longer lazy.

// https://github.com/expressjs/multer
const multer = require('multer');
const storage = multer.memoryStorage(); // we might have to change this
const multerUpload = multer({ storage: storage });

export class ManualRoute {
    public static create(app: any) {    
        app.post('/api/foo', multerUpload.single('binarydata'),
            (req: Request, res: Response, next: NextFunction) => {
           new BinaryDeviceRoute().upload(req, res, next);
        })
    }

    public async upload(reqorig: Request, res: Response, next: NextFunction) {
        const file: any = req.file;

        if (file == null || file === undefined) {
            next(new Error('missing data file'));
            return;
        }

        let data: Buffer = file.buffer;
        res.status(200);
        res.send('OK');
    }
}

https://swagger.io/docs/specification/describing-responses/#response-that-returns-a-file

Version 3.0 of the Swagger OpenApi still supports file downloads. What version of the OAS will TSOA support in the future?

I managed to get a file upload with TSOA, multer and Aurelia running. Looks good. But now I dig into the download topic and found that there seems to be no way to get the Express response in the TSOA controller. So I could not set a content type or put a file into it. Any suggestions?

The way to go here, for now, is to use a custom route template. In an app I use tsoa on, you can download CSVs, and there's a special route template to handle that case.

That said, it's pretty hacky, and I would like to implement first class support (or accept a pull request that does it)

There is a PR open for almost a year adding file download to TSOA. It has merge conflicts. I fixed the conflicts and opened a new PR: https://github.com/lukeautry/tsoa/pull/228

You might close other issues too, since the original unit tests show how to download a HTML file (I saw one or two request) with the new addition.

@lukeautry can we merge this?

Wonder if this is merged? looking a way to export file and download it. Any documents? I couldn't locate it. Thanks

No, PR #228 is not merged yet.

@lukeautry
When will this feature be merged?

https://blog.nubix.de/index.php/2018/09/21/file-download-and-upload-with-tsoa-hack/

I put an example online demonstrating how to achieve upload and download files with the current version of TSOA. Downloading is a little bit dirty since file responses are not handled yet by TSOA.

I hope the example might help some user here.

Any news about this issue?

I’m one of the new maintainers, and I’d love to help you guys out but (a) I have never had to send a file back from an API so so don’t feel qualified to review this PR and (b) the PR needs to be rebased again.

But I promise you that if someone else rebases it and gives it a solid review then I will merge it and push the new version to npm.

I do have one concern though, does it make sense to allow any string value for the produces decorator? Isn’t there a discrete set of legal options? (if I’m right then we shouldn’t accept a string we should accept a union of those string literals).

Update: I did review the PR and there are a few things missing. Before we support this officially, we need to make sure that we are at least generating the correct swagger models. If we can’t do that then we’re not offering anything different than the workaround (which is to have a custom route that lives outside of tsoa) since neither would be providing swagger generation. That being said, I don’t want anyone to be discouraged— the PR is very close, it just need someone to commit to starting and completing the swagger generation parts. When they do, I will approve and merge it.

@ryo-nbx Hey, I am looking into the workaround solution you provided above. It is looking good. Would you please publish a sample project for the workaround solution.

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days

Greetings, any news yet on if there could be a proper documentation for creating a controller with an action for handling file upload with TSOA?

No news needed, it's been documented here

Was this page helpful?
0 / 5 - 0 ratings

Related issues

rustam-crunch picture rustam-crunch  Â·  5Comments

amcdnl picture amcdnl  Â·  6Comments

Izabrr picture Izabrr  Â·  4Comments

jeremyVignelles picture jeremyVignelles  Â·  3Comments

strongpauly picture strongpauly  Â·  3Comments