Fresh logo

Forms

For stronger resiliency and user experience, Fresh relies on native browser support for form submissions with the HTML <form> element.

In the browser, a <form> submit will send an HTML action (usually GET or POST) to the server, which responds with a new page to render.

POST request with application/x-www-form-urlencoded

Forms typically submit as a GET request with data encoded in the URL’s search parameters, or as a POST request with either an application/x-www-form-urlencoded or multipart/form-data body.

This example demonstrates how to handle application/x-www-form-urlencoded <form> submissions:

Typescript routes/subscribe.tsx
import { define } from "../utils.ts";

export const handlers = define.handlers({
  async GET(ctx) {
    return { data: {} };
  },
  async POST(ctx) {
    const form = await ctx.req.formData();
    const email = form.get("email")?.toString();

    // Add email to list.

    // Redirect user to thank you page.
    const headers = new Headers();
    headers.set("location", "/thanks-for-subscribing");
    return new Response(null, {
      status: 303, // See Other
      headers,
    });
  },
});

export default define.page<typeof handlers>(function Subscribe() {
  return (
    <>
      <form method="post">
        <input type="email" name="email" value="" />
        <button type="submit">Subscribe</button>
      </form>
    </>
  );
});

When the user submits the form, Deno will retrieve the email value using the request’s formData() method, add the email to a list, and redirect the user to a thank you page.

Handling file uploads

File uploads can be handled in a very similar manner to the example above. Note that this time, we have to explicitly declare the form’s encoding to be multipart/form-data.

Typescript routes/subscribe.tsx
import { define } from "../utils.ts";

export const handler = define.handlers({
  async GET(ctx) {
    return { data: { message: null } };
  },
  async POST(ctx) {
    const form = await ctx.req.formData();
    const file = form.get("my-file") as File;

    if (!file) {
      return { data: { message: "Please try again" } };
    }

    const name = file.name;
    const contents = await file.text();

    console.log(contents);

    return { data: { message: `${name} uploaded!` } };
  },
});

export default define.page<typeof handlers>(function Upload(props) {
  const { message } = props.data;
  return (
    <>
      <form method="post" encType="multipart/form-data">
        <input type="file" name="my-file" />
        <button type="submit">Upload</button>
      </form>
      {message ? <p>{message}</p> : null}
    </>
  );
});

A note of caution

These examples are simplified to demonstrate how Deno and Fresh handle HTTP requests. In the Real World™, you’ll want to validate your data (especially the file type) and protect against cross-site request forgery. Consider yourself warned.