feat: add multipart/form-data and file upload support - #357
Open
GFoniX wants to merge 6 commits into
Open
Conversation
Author
|
Hey @BenLorantfy, just a friendly ping on this PR! It's been open for a couple of months now, so I wanted to see if this is something you'd still be interested in integrating. Let me know if you need any adjustments or if I should resolve any merge conflicts. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #128
Summary
This PR adds first-class
multipart/form-dataand file upload support tonestjs-zod, resolving a long-standing gap tracked in #128.Until now, using NestJS with Multer and Zod required bespoke glue code: manually merging uploaded files into the body, hand-rolling bracket-notation parsing, and writing custom validators for file constraints. This PR makes all of that a first-class part of the library.
What was added
ZodMultipartInterceptor— drop-in multipart supportA new NestJS interceptor that does three things automatically:
multipart/form-datarequests usingmulter(only invoked when the body has not already been parsed, so it does not interfere withapplication/jsonroutes that share the same controller).req.bodyunder theirfieldname, so they flow naturally into the DTO validated byZodValidationPipe.parseFormDatato convert flat bracket-notation keys into a nested object and to auto-parse JSON strings.Usage is a single decorator:
zMulterFile()— typed file field schemaA Zod schema factory that validates that the field value is a real Multer file object (
Express.Multer.File). It exposes a fluent API for declaring constraints that are also reflected in the generated OpenAPI/Swagger document:Constraint methods:
.mimeType(type | type[]).maxSize(size)'5MB','10Mo','1Go').minSize(size)Supported size units:
b,o,ko/kb,mo/mb,go/gb,to/tb(case-insensitive).parseFormData()— bracket-notation body parsingA standalone utility that converts a flat Multer/form body using bracket notation into a proper nested object, with automatic JSON string parsing for object/array values:
ZodMultipartInterceptorcalls this automatically, but it is also exported for advanced use cases.Example app: new Missions module
The example app (
packages/example) has been extended with a full Missions module that demonstrates the complete multipart feature set in a realistic scenario:missions.dto.ts— aCreateMissionDtowith nested objects, arrays of objects, and both optional and required file fields.missions.controller.ts— aPOST /api/missionsendpoint that accepts bothmultipart/form-dataandapplication/json, with a workingcurlexample in the Swagger description.Swagger-compatibility helpers:
zFormJsonandzFormArraySwagger UI has a quirk when working with
multipart/form-data: even though bracket-notation field parsing handles most cases, Swagger UI sometimes sends nested objects and arrays as raw JSON strings rather than as individual form fields.To handle this transparently, the example app introduces two small
z.preprocesshelpers inform-helpers.ts:zFormJson(schema)— wraps any Zod schema so that if the raw value is a JSON-looking string, it is parsed before validation. Used for single nested objects sent as a JSON string by Swagger UI (e.g.coordinates).zFormArray(arraySchema)— wraps az.array(…)schema to handle: a real array (pass-through), a JSON array string, a single non-array value wrapped into[value], or empty →[]. Used for arrays of objects that Swagger UI may send as a JSON string.These helpers live in the example app only (not exported from the main package) because they are thin one-liner wrappers over
z.preprocessthat are trivial to copy. The core bracket-notation parsing required by standard HTTP clients (curl, Axios,fetchwithFormData) is handled fully automatically byZodMultipartInterceptor.Exports added to
nestjs-zodChecklist
parseFormDatahas unit tests (parse-form-data.test.ts)multeris a peer dependency (already installed in typical NestJS projects)multipart/form-dataandapplication/jsonon the same routetype: string, format: binary