HTTP Forms

  • http
  • javascript
  • typescript
Aug 20, 2026
HTTP forms using the JavaScript Fetch API.

HTML

HTML forms are the fundamental building block that allow users to submit data to a web server. HTML forms can be submitted as either a GET or POST request (other HTTP methods are not supported).

The action attribute specifies the URL or pathname of the route that handles the submission.

<form method="POST" action="/post-handler">
	<div>
		<label for="name">name</label>
		<input id="name" type="text" name="name" />
	</div>
	<div>
		<label for="title">title</label>
		<input id="title" type="text" name="title" />
	</div>
	<button>Submit</button>
</form>

HTML forms can be submitted without client side JavaScript, or programmatically with JavaScript.

document.querySelector("form").requestSubmit(); // uses the form's current method and enctype

Request

Submitting the form creates a Request to the route specified in the action.

For POST requests, request.body is a ReadableStream<Uint8Array> that streams the values the user submitted in the form.

For example, if a user submits the form above with name set to ross and title set to developer, the raw request body can be read with await req.text().

const text = await req.text();

The buffered text from the request would look like this:

name=ross&title=developer

For GET requests, the inputs are added to the URL as URLSearchParams.

If the same form were submitted with method="GET", req.url would contain the query string.

https://example.com/post-handler?name=ross&title=developer

You can then parse the URL and read each value from searchParams.

const url = new URL(req.url);

console.log(url.searchParams.get("name"));
// ross

console.log(url.searchParams.get("title"));
// developer

Multipart

The enctype attribute can be used to change the encoding type of the form submission. Most commonly, enctype="multipart/form-data" allows forms to be submitted with File inputs.

<form method="POST" action="/post-handler" enctype="multipart/form-data">
	<div>
		<label for="name">name</label>
		<input id="name" type="text" name="name" />
	</div>
	<div>
		<label for="title">title</label>
		<input id="title" type="text" name="title" />
	</div>
	<div>
		<label for="video">video</label>
		<input id="video" type="file" name="video" accept="video/*" />
	</div>
	<button>Submit</button>
</form>

If you build a FormData object and send it with fetch, the browser sends it as multipart/form-data automatically regardless of the form’s enctype attribute.

const form = document.querySelector("form");

await fetch("/post-handler", { method: "POST", body: new FormData(form) });

Instead of encoding the body as a single query string, multipart submissions split the request into sections separated by a boundary. This boundary is sent in the Content-Type header of the request for the server to use to find where each part starts and ends.

Content-Type: multipart/form-data; boundary=----formBoundary123

If the user uploads a large video file named video.mp4, the file part can take a while to load across the network. This is why large uploads are better handled as a stream instead of being buffered all at once.

------formBoundary123
Content-Disposition: form-data; name="name"

ross
------formBoundary123
Content-Disposition: form-data; name="title"

developer
------formBoundary123
Content-Disposition: form-data; name="video"; filename="video.mp4"
Content-Type: video/mp4

megabytes of binary video data...
------formBoundary123--

Server

Web servers can read the request created from the form. The incoming Request object is made available modern JavaScript servers typically on the argument of the request handler. For example, Hono makes the Request available on the Context within each middleware.

Form data

The Request.formData method is a built-in method available to parse the body of a POST request. It works the same regardless of the enctype attribute of the form.

The main disadvantage of using this method is that it forces you to load the entire body of the request into your server memory instead of being able to stream through your server to another source.

await req.formData();

Search params

For GET requests, the data is contained within the Request.url string directly instead of within the body. The easiest way to obtain the data is creating a URL object with the URL string, and then accessing the searchParams property from there.

new URL(req.url).searchParams;

Security

Since users are able to submit any sort of form data or search parameters to your server, you must treat them as untrusted data.

  • Invalid values - for example, if they are too long or the wrong format
  • Extra data - attackers can send extra keys that your server doesn’t expect
  • Large requests - overload server with a very large file

Frameworks provide a variety of ways to validate user inputs. Primarily libraries like Zod allow users to validate inputs at runtime to ensure data is the correct type.

Parsers

There are a variety of packages that help servers efficiently and securely parse and stream form data. Check out my cross platform, low memory multipart parser here.

Edit