@remix-run/form-data-middleware 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Shopify Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
package/README.md ADDED
@@ -0,0 +1,79 @@
1
+ # form-data-middleware
2
+
3
+ Middleware for parsing [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) from incoming request bodies for use with [`@remix-run/fetch-router`](https://github.com/remix-run/remix/tree/main/packages/fetch-router).
4
+
5
+ ## Installation
6
+
7
+ ```sh
8
+ npm install @remix-run/form-data-middleware
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ Use the `formData()` middleware at the router level to parse `FormData` from the request body and make it available on the request context as `context.formData`.
14
+
15
+ `context.files` will also be available as a map of `File` objects keyed by the name of the form field.
16
+
17
+ ```ts
18
+ import { createRouter } from '@remix-run/fetch-router'
19
+ import { formData } from '@remix-run/form-data-middleware'
20
+
21
+ let router = createRouter({
22
+ middleware: [formData()],
23
+ })
24
+
25
+ router.post('/users', async (context) => {
26
+ let name = context.formData.get('name')
27
+ let email = context.formData.get('email')
28
+
29
+ // Handle file uploads
30
+ let avatar = context.files?.get('avatar')
31
+
32
+ return Response.json({ name, email, hasAvatar: !!avatar })
33
+ })
34
+ ```
35
+
36
+ ### Custom File Upload Handler
37
+
38
+ You can use a custom upload handler to customize how file uploads are handled. The return value of the upload handler will be used as the value of the form field in the `FormData` object.
39
+
40
+ ```ts
41
+ import { formData } from '@remix-run/form-data-middleware'
42
+ import { writeFile } from 'node:fs/promises'
43
+
44
+ let router = createRouter({
45
+ middleware: [
46
+ formData({
47
+ async uploadHandler(upload) {
48
+ // Save to disk and return path
49
+ let path = `./uploads/${upload.name}`
50
+ await writeFile(path, Buffer.from(await upload.arrayBuffer()))
51
+ return path
52
+ },
53
+ }),
54
+ ],
55
+ })
56
+ ```
57
+
58
+ ### Suppress Parse Errors
59
+
60
+ Some requests may contain invalid form data that cannot be parsed. You can suppress parse errors by setting `suppressErrors` to `true`. In these cases, `context.formData` will be an empty `FormData` object.
61
+
62
+ ```ts
63
+ let router = createRouter({
64
+ middleware: [
65
+ formData({
66
+ suppressErrors: true, // Invalid form data won't throw
67
+ }),
68
+ ],
69
+ })
70
+ ```
71
+
72
+ ## Related Packages
73
+
74
+ - [`fetch-router`](https://github.com/remix-run/remix/tree/main/packages/fetch-router) - Router for the web Fetch API
75
+ - [`form-data-parser`](https://github.com/remix-run/remix/tree/main/packages/form-data-parser) - The underlying form data parser
76
+
77
+ ## License
78
+
79
+ See [LICENSE](https://github.com/remix-run/remix/blob/main/LICENSE)
@@ -0,0 +1,4 @@
1
+ export { FormDataParseError } from '@remix-run/form-data-parser';
2
+ export type { FileUpload, FileUploadHandler } from '@remix-run/form-data-parser';
3
+ export { type FormDataOptions, formData } from './lib/form-data.ts';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAA;AAChE,YAAY,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAA;AAEhF,OAAO,EAAE,KAAK,eAAe,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { FormDataParseError } from '@remix-run/form-data-parser';
2
+ export { formData } from "./lib/form-data.js";
@@ -0,0 +1,21 @@
1
+ import { type FileUploadHandler, type ParseFormDataOptions } from '@remix-run/form-data-parser';
2
+ import type { Middleware } from '@remix-run/fetch-router';
3
+ export interface FormDataOptions extends ParseFormDataOptions {
4
+ /**
5
+ * Set `true` to suppress parse errors. Default is `false`.
6
+ */
7
+ suppressErrors?: boolean;
8
+ /**
9
+ * A function that handles file uploads. It receives a `FileUpload` object and may return any
10
+ * value that is a valid `FormData` value. Default is `undefined`, which means file uploads are
11
+ * stored in memory.
12
+ */
13
+ uploadHandler?: FileUploadHandler;
14
+ }
15
+ /**
16
+ * Middleware that parses `FormData` from the request body and populates `context.formData`.
17
+ * @param options Options for parsing form data
18
+ * @returns A middleware function that parses form data
19
+ */
20
+ export declare function formData(options?: FormDataOptions): Middleware;
21
+ //# sourceMappingURL=form-data.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"form-data.d.ts","sourceRoot":"","sources":["../../src/lib/form-data.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EAC1B,MAAM,6BAA6B,CAAA;AAEpC,OAAO,KAAK,EAAE,UAAU,EAAkB,MAAM,yBAAyB,CAAA;AAEzE,MAAM,WAAW,eAAgB,SAAQ,oBAAoB;IAC3D;;OAEG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB;;;;OAIG;IACH,aAAa,CAAC,EAAE,iBAAiB,CAAA;CAClC;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,OAAO,CAAC,EAAE,eAAe,GAAG,UAAU,CA8B9D"}
@@ -0,0 +1,31 @@
1
+ import { FormDataParseError, parseFormData, } from '@remix-run/form-data-parser';
2
+ /**
3
+ * Middleware that parses `FormData` from the request body and populates `context.formData`.
4
+ * @param options Options for parsing form data
5
+ * @returns A middleware function that parses form data
6
+ */
7
+ export function formData(options) {
8
+ let suppressErrors = options?.suppressErrors ?? false;
9
+ let uploadHandler = options?.uploadHandler;
10
+ return async (context) => {
11
+ // Get the method from context to respect any method override middleware
12
+ let method = context.method;
13
+ if (method === 'GET' || method === 'HEAD') {
14
+ return;
15
+ }
16
+ let contentType = context.headers.get('Content-Type');
17
+ if (contentType == null ||
18
+ (!contentType.startsWith('multipart/') &&
19
+ !contentType.startsWith('application/x-www-form-urlencoded'))) {
20
+ return;
21
+ }
22
+ try {
23
+ context.formData = await parseFormData(context.request, options, uploadHandler);
24
+ }
25
+ catch (error) {
26
+ if (!suppressErrors || !(error instanceof FormDataParseError)) {
27
+ throw error;
28
+ }
29
+ }
30
+ };
31
+ }
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@remix-run/form-data-middleware",
3
+ "version": "0.0.0",
4
+ "description": "Middleware for parsing FormData from request bodies",
5
+ "author": "Michael Jackson <mjijackson@gmail.com>",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/remix-run/remix.git",
10
+ "directory": "packages/form-data-middleware"
11
+ },
12
+ "homepage": "https://github.com/remix-run/remix/tree/main/packages/form-data-middleware#readme",
13
+ "files": [
14
+ "LICENSE",
15
+ "README.md",
16
+ "dist",
17
+ "src",
18
+ "!src/**/*.test.ts"
19
+ ],
20
+ "type": "module",
21
+ "exports": {
22
+ ".": {
23
+ "types": "./dist/index.d.ts",
24
+ "default": "./dist/index.js"
25
+ },
26
+ "./package.json": "./package.json"
27
+ },
28
+ "devDependencies": {
29
+ "@types/node": "^24.6.0",
30
+ "typescript": "^5.9.3",
31
+ "@remix-run/fetch-router": "0.9.0",
32
+ "@remix-run/form-data-parser": "0.14.0"
33
+ },
34
+ "peerDependencies": {
35
+ "@remix-run/fetch-router": "^0.9.0",
36
+ "@remix-run/form-data-parser": "^0.14.0"
37
+ },
38
+ "keywords": [
39
+ "fetch",
40
+ "router",
41
+ "middleware",
42
+ "form-data",
43
+ "multipart",
44
+ "file-upload"
45
+ ],
46
+ "scripts": {
47
+ "build": "tsc -p tsconfig.build.json",
48
+ "clean": "git clean -fdX",
49
+ "test": "node --disable-warning=ExperimentalWarning --test './src/**/*.test.ts'",
50
+ "typecheck": "tsc --noEmit"
51
+ }
52
+ }
package/src/index.ts ADDED
@@ -0,0 +1,5 @@
1
+ export { FormDataParseError } from '@remix-run/form-data-parser'
2
+ export type { FileUpload, FileUploadHandler } from '@remix-run/form-data-parser'
3
+
4
+ export { type FormDataOptions, formData } from './lib/form-data.ts'
5
+
@@ -0,0 +1,58 @@
1
+ import {
2
+ FormDataParseError,
3
+ parseFormData,
4
+ type FileUploadHandler,
5
+ type ParseFormDataOptions,
6
+ } from '@remix-run/form-data-parser'
7
+
8
+ import type { Middleware, RequestContext } from '@remix-run/fetch-router'
9
+
10
+ export interface FormDataOptions extends ParseFormDataOptions {
11
+ /**
12
+ * Set `true` to suppress parse errors. Default is `false`.
13
+ */
14
+ suppressErrors?: boolean
15
+ /**
16
+ * A function that handles file uploads. It receives a `FileUpload` object and may return any
17
+ * value that is a valid `FormData` value. Default is `undefined`, which means file uploads are
18
+ * stored in memory.
19
+ */
20
+ uploadHandler?: FileUploadHandler
21
+ }
22
+
23
+ /**
24
+ * Middleware that parses `FormData` from the request body and populates `context.formData`.
25
+ * @param options Options for parsing form data
26
+ * @returns A middleware function that parses form data
27
+ */
28
+ export function formData(options?: FormDataOptions): Middleware {
29
+ let suppressErrors = options?.suppressErrors ?? false
30
+ let uploadHandler = options?.uploadHandler
31
+
32
+ return async (context: RequestContext) => {
33
+ // Get the method from context to respect any method override middleware
34
+ let method = context.method
35
+
36
+ if (method === 'GET' || method === 'HEAD') {
37
+ return
38
+ }
39
+
40
+ let contentType = context.headers.get('Content-Type')
41
+
42
+ if (
43
+ contentType == null ||
44
+ (!contentType.startsWith('multipart/') &&
45
+ !contentType.startsWith('application/x-www-form-urlencoded'))
46
+ ) {
47
+ return
48
+ }
49
+
50
+ try {
51
+ context.formData = await parseFormData(context.request, options, uploadHandler)
52
+ } catch (error) {
53
+ if (!suppressErrors || !(error instanceof FormDataParseError)) {
54
+ throw error
55
+ }
56
+ }
57
+ }
58
+ }