# mist is a Gleam HTTP server with websockets, chunked responses and file serving

> mist is an HTTP server for the Gleam language, published on Hex and built on gleam_erlang and gleam_http, with a builder-style API that covers routing by path segment, websockets, chunked transfer, streaming request bodies and file serving. It suits a Gleam developer who wants a server without a framework, and its last tagged release was in 2022 while the repository is still being touched.

**rawhat/mist** — gleam HTTP server. because it glistens on a web

- Repository: https://github.com/rawhat/mist
- Stars: 529 · Forks: 38
- Language: Gleam
- License: Apache-2.0
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/rawhat-mist

## The builder is the whole configuration surface

mist is a Gleam HTTP server, described as a glistening Gleam web server, and the way you configure it is the way you would expect from a language that likes pipes.

The main entrypoint for your application is `mist.start`. The argument to that function is generated from the opaque `Builder` type. It can be constructed with the `mist.new` function and fed updated configuration options with the associated methods.

```gleam
    |> mist.new
    |> mist.bind("localhost")
    |> mist.with_ipv6
    |> mist.port(4000)
    |> mist.start
```

That chain is the configuration. You pass the request handler into mist.new, set the bind address, opt into IPv6, set the port and start. Each option is a method returning a new builder, so the shape of the server is readable top to bottom without a separate options struct.

The handler itself is a function from a Request to a Response, and the request type is parameterised by a Connection, which is how the library lets you read connection metadata such as the peer address.

The example that ships in the README is a complete server in one file, and it is worth reading as a catalogue of what the library covers: it serves an HTML index, responds to an echo route, handles a websocket route, streams a chunked response, serves files from a path and handles a streamed request body, with a not-found response for everything else.

## Adding it to a project

The README gives the setup as a sequence of Gleam commands, and it is the whole installation guide.

```sh
$ gleam new <your_project>
$ cd <your_project>
$ gleam add mist logging gleam_erlang gleam_http
```

Three dependencies come in with it, and each one tells you something about how the library is built.

gleam_erlang is the bridge to the Erlang runtime, and it is not optional decoration: a Gleam HTTP server that supports websockets and long-lived connections is doing its work in processes, and mist is using the Erlang side to do it.

gleam_http is the HTTP types package, providing Request and Response types, and the example imports gleam/http/request and gleam/http/response directly, so the shapes you work with are the standard ones from that package rather than mist-specific types.

logging is a separate dependency because the example uses it to report connection info and received websocket frames, with logging.configure and logging.set_level(logging.Debug) at the start of main.

The imports in the example give the rest of the picture. gleam/bit_array and gleam/bytes_tree for body handling, gleam/erlang/process for the Subject type used by the chunked response, gleam/int, gleam/io, gleam/list, gleam/option, gleam/result and gleam/string for the ordinary work of a program that also happens to serve HTTP.

The fact that the dependency list is this short is the main argument for the library. There is no framework layer to learn and no configuration file format, just Gleam packages you would install anyway.

## Websockets, chunking and streaming, which is where it earns its keep

The example server handles three non-trivial protocol cases and they are the clearest evidence of what the library does beyond plain request and response.

The websocket route is built with `mist.websocket`, taking a request, an on_init callback, an on_close callback and a handler. The handler receives a state, a message and a connection, and the library offers `mist.send_text_frame` and a `mist.continue` to keep the conversation going, `mist.stop` to end it, and variants for binary frames and a custom message type.

The chunked response is built with `mist.chunked`, taking the request, a response, an initialiser that spawns a process, and a callback that receives a state, a message and the connection. The message type is a custom variant with a Data and a Done case, and the callback either sends a chunk with `mist.send_chunk` and continues, or calls `mist.chunk_stop` when it sees Done.

The stream route uses `mist.stream`, which returns an Ok with a consume function or an Error. The consume function takes a byte count and returns either a Chunk carrying the data and the next consume function, or Done, and the example recurses on it accumulating into a BitArray.

What ties these together is that they all use the Erlang process model rather than threads. The chunked example spawns a process with `process.spawn` and sends it messages through a `Subject`, and the sleep between chunks in the demo is `process.sleep(2000)`. That is the architecture of an Erlang HTTP server, and it is why the library can offer streaming and long-lived connections without the complexity a threaded design would need.

## File serving and body reading, with the sharp edges left in

The example includes a file route and an echo route, and both show how the library handles the parts of HTTP that usually get hand-rolled badly.

File serving is `mist.send_file(file_path, offset: 0, limit: None)`, which returns a Result that you map into a response with the content type guessed from the path, and unwrap into a 404 when the file is not there. The example has a comment noting that validation is omitted for brevity, which is a useful warning: `mist.send_file` takes a path, and the path comes from the request, so validating it is the caller's job.

Body reading is `mist.read_body(request, 1024 * 1024 * 10)`, with a size limit in bytes, returning a Result that you map into a response echoing the body with the content type taken from the request header.

One thing that jumps out from the echo example is the content type handling. It calls `request.get_header('content-type')` and then `result.unwrap('text/plain')`, which unwraps to a plain text fallback when the header is missing. The unwrap-on-error shape there means a missing content type does not crash the request, and the 400 branch is for a body read that fails rather than for a missing header.

The example also shows how headers are set, with `response.prepend_header` used for the response headers in the index route, which is worth noting because it is the API for adding rather than replacing, so calling it twice for the same header name adds both values.

## The examples directory and the repository state

The repository is small and standard for a Gleam package. There is src/ for the source, test/ for the tests, examples/ for the sample projects, a gleam.toml and a manifest.toml for the package definition, a CHANGELOG.md, a LICENSE and a .github/ directory.

The three example projects are the most informative thing in the repository, because they each demonstrate a different shape of server.

There is examples/complete/, which is the full server from the README turned into a project you can run. There is examples/eventz/, and the name suggests the server-sent events case, which is the other long-lived connection style and the one that distinguishes a good HTTP server from a basic one. And there is examples/over_http2/, which covers the HTTP/2 path.

That third one matters for the HTTP/2 support, because a server that speaks HTTP/2 needs different handling of streams, and the presence of a dedicated example suggests it is a supported configuration rather than an accident of the underlying Erlang libraries.

The project is Apache-2.0 licensed, the primary language is Gleam, and it is published on Hex as the mist package with documentation on HexDocs, both linked from the README badges.

The version history is where the picture gets complicated. The listed releases are 0.2.0 from 2022-05-20 and 0.1.3 from 2022-04-19, and the last push to the repository was on 2026-04-18. So the code has been touched recently while the tags have not moved in four years, which is the pattern you get when a package is maintained informally through the repository rather than through released versions.

## Where mist is the wrong choice

Three cases argue against it.

The first is a project that needs a released, supported version. The last tagged release is 0.2.0 from 2022. If your deployment pins a version from Hex, you are pinning a four-year-old tag, and any fix that landed since then on master is not in your build. That may be fine, because the code is Apache-2.0 and you can point your dependency at the repository, but it means you are opting into an unreleased dependency for a server, which is a different risk profile from a library that is not on the request path.

The second is a team that wants routing, middleware and a project template. mist is a server, not a framework. The example does its own path matching with a case on `request.path_segments`, its own 404 handling, and its own logging setup. There is no router abstraction, no middleware pipeline and no project scaffolding, which is exactly what the Gleam ecosystem's own conventions provide, so this is a deliberate line in the sand rather than an omission.

The alternative in the same direction is a Gleam framework with a web layer, or, if you are comfortable in the Erlang world, Cowboy or Plug. The difference in approach is that mist gives you the HTTP and websocket protocol handling and leaves routing and application structure to you, so the shape of your server is entirely visible in your own code rather than configured by convention. Cowboy and Plug are more mature and better documented, and Plug in particular has a large ecosystem of middleware, so the choice comes down to whether you want to work in Gleam's type system against a thin server, or accept a larger Erlang framework and its conventions. For a Gleam-native service, mist is the lower-friction of the two.

## Conclusion

mist fits a Gleam developer who wants an HTTP server with the protocol details already handled, particularly for websocket and streaming work where the Erlang ecosystem gives it an advantage. It does not fit a project that needs a maintained framework with regular releases, because the last tagged release was 0.2.0 in May 2022 even though the repository was pushed to on 2026-04-18. Add it with `gleam add mist logging gleam_erlang gleam_http`, pin the version you depend on, and read `src/` and the `examples/` directory, which includes complete, eventz and over_http2 projects.

## FAQ

### What is mist?

It is an HTTP server for the Gleam language, published on Hex as the mist package. It provides a builder-style API for configuring the server and handles websockets, chunked responses, streamed request bodies and file serving.

### How do I add mist to a Gleam project?

Create the project with `gleam new <your_project>`, change into it and run `gleam add mist logging gleam_erlang gleam_http`. The three dependencies are the server itself, the Erlang bridge it is built on, the HTTP types package and a logging library.

### How is a mist server configured?

Through an opaque Builder type. You construct it with `mist.new` given your handler, then pipe it through options such as `mist.bind("localhost")`, `mist.with_ipv6` and `mist.port(4000)` before calling `mist.start`.

### Does mist support websockets and streaming?

Yes. The example server handles a websocket route with `mist.websocket` and its send_text_frame and continue helpers, a chunked response with `mist.chunked` backed by an Erlang process and a Subject, a streamed request body with `mist.stream`, and file serving with `mist.send_file`.

### What is the latest release of mist?

The listed releases are 0.2.0 from 2022-05-20 and 0.1.3 from 2022-04-19. The last push to the repository was on 2026-04-18, so the source has been touched more recently than the tags suggest.

## Sources

- [Issues](https://github.com/rawhat/mist/issues)
- [License: Apache-2.0](https://github.com/rawhat/mist/blob/master/LICENSE)
- [rawhat/mist on GitHub](https://github.com/rawhat/mist)
- [README](https://github.com/rawhat/mist/blob/master/README.md)
- [Releases](https://github.com/rawhat/mist/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/rawhat-mist
