multipartkit

HexHex DownloadsCI

A Gleam library for parsing, querying, validating, and building multipart messages on Erlang/BEAM and JavaScript targets. Primary target: multipart/form-data. Secondary: multipart/mixed and multipart/related per RFC 2046 ยง5.1.1 grammar.

Install

gleam add multipartkit

For the streaming API you also need gleam_yielder:

gleam add gleam_yielder

Quick start

The snippet below is the source of examples/quick_start verbatim; copy it into src/main.gleam, run gleam add multipartkit, then gleam run.

import gleam/io
import gleam/option.{None, Some}
import multipartkit
import multipartkit/form
import multipartkit/query

pub fn main() {
  let request_form =
    form.new()
    |> form.add_field("title", "hello")
    |> form.add_file("avatar", "cat.png", "image/png", <<137, 80, 78, 71>>)

  let #(content_type, body) = multipartkit.encode_form(request_form)

  let assert Ok(parts) = multipartkit.parse(body, content_type)
  let assert Ok(title) = query.required_field(parts, "title")
  let assert Ok(avatar) = query.required_file(parts, "avatar")

  io.println("Content-Type: " <> content_type)
  io.println("title=" <> title)
  case avatar.filename {
    Some(filename) -> io.println("avatar filename=" <> filename)
    None -> io.println("avatar has no filename")
  }
}

Examples

The examples/ directory has four self-contained Gleam projects you can clone, run, and modify:

Example Use case
quick_start Encode a Form, parse it back, pull a field and a file.
parse_request Parse an incoming HTTP request body, with strict Limits and validate.allowed_content_types.
streaming_parse Feed input through parse_stream chunk-by-chunk and observe max_body_bytes being enforced incrementally.
mimetype_inference Wire nao1215/mimetype into add_file_auto_with to infer Content-Type.
cd examples/<name>
gleam run

Run them all from the repo root with just examples.

Streaming caveat (v0.1.0)

parse_stream pulls input chunks lazily and enforces max_body_bytes incrementally, so an oversized stream is rejected at the chunk that crosses the limit, not after the whole body is buffered. However, each StreamPart.body is materialised as a single buffered chunk before the part is yielded; per-part memory is bounded by max_part_bytes rather than by an arbitrary chunk size. True chunk-by-chunk body streaming is on the roadmap but is not part of v0.1.0.

Public modules

multipartkit (facade), multipartkit/parser, multipartkit/encoder, multipartkit/form, multipartkit/query, multipartkit/stream, multipartkit/validate, multipartkit/content_disposition, multipartkit/header, multipartkit/infer, multipartkit/limit, multipartkit/error, multipartkit/part.

Contributing / development

The repo uses mise for toolchain pinning and just for task running:

mise trust .mise.toml
mise install
just deps
just ci

just recipes source scripts/lib/mise_bootstrap.sh so mise activate is not required in the current shell. See CONTRIBUTING.md for the full workflow.

License

Released under the MIT License โ€” see the file for details.