Dropshot: a Rust crate for exposing REST APIs, with one opinion
expose REST APIs from a Rust program
At a glance
- What is it?
- Dropshot is Oxide Computer's general-purpose crate for exposing REST APIs from a Rust program, split into a runtime crate and a procedural macro for registering handlers. Its distinguishing design choice is that there is deliberately no way to register a handler that runs on every request, and the README explains at length why.
- Who is it for?
- Adopt Dropshot if you are building an API server in Rust and want typed handler registration, a documented configuration format and a tool that assumes an API is a public contract rather than a set of endpoints. Do not adopt it if your design depends on middleware that runs around every request, because the crate deliberately has no equivalent, and the workaround the project proposes is a pattern you write yourself.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 4 days ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Two packages: the runtime and the registration macro
Dropshot is described as a general-purpose crate for exposing REST APIs from a Rust program, and the repository is built as two packages. The Cargo.toml is explicit that consumers only need to know about dropshot, and that dropshot depends on dropshot_endpoint, a procedural macro used for registering API handler functions. The workspace lists both as members and default members, uses resolver 2, and sets the shared edition to 2024 with a rust-version of 1.88. That second package is the interesting design decision in miniature. A macro that generates the boilerplate for a handler means the endpoint declaration and the compiler-checked signature live together, so a change to the request and response types is a compile error rather than a runtime surprise. The tree adds the usual supporting files, a CHANGELOG.adoc, a MAINTAINERS.adoc for maintainers handling releases and dependabot, release.toml, rust-toolchain.toml and rustfmt.toml. The documentation itself is on docs.rs/dropshot, and you can build it locally with cargo +nightly doc.
The TOML configuration, and the defaults that will bite you
Dropshot servers are configured with a TOML file, and the reference in the README is the specification. bind_address specifies the IP address and TCP port the server binds to, with the example 127.0.0.1:12220 and a default of 127.0.0.1:0, and the documentation is careful to say that in general servers can bind to more than one address and port but that this is not yet supported. default_request_body_max_bytes sets the maximum number of bytes allowed in a request body, with an example of 4096 and a default of 1024, larger requests receiving a 400 error, and it can be overridden per endpoint through the request_body_max_bytes parameter to the endpoint attribute. That default of 1024 bytes is small enough to be worth knowing before you point a real client at the service, and the per-endpoint override is the answer for the endpoints that need more. The other settings are optional and describe where the server listens and how it reacts to oversized bodies, and the whole set is a handful of properties rather than a framework of nested sections.
TLS in two shapes, and both of them change the listener
The TLS configuration is the part with a detail that is easy to skim past and expensive to get wrong. tls.type specifies how certificate and key information is provided, with valid values including AsFile and AsBytes. The AsFile variant takes tls.cert_file, the path to a PEM file containing a certificate chain for the server to identify itself with, where the first certificate is the end-entity certificate and the rest are intermediates on the way to a trusted CA, and tls.key_file, the path to a PEM-encoded PKCS #8 file holding the private key. The AsBytes variant is identical but given as buffers, with tls.certs and tls.key. The consequence to note is in the description of both file-based options: if specified, the server will only listen for TLS connections. That is not a detail about certificates, it is a detail about the listener, and a configuration that sets a certificate but expects plain HTTP on the same port will not behave the way you might assume. The project is at least consistent about stating it in both places.
Logging you can use without the rest of the framework
The logging section covers a small wrapper to configure a slog-based Logger, and it makes a point worth repeating: you can use this without using the rest of Dropshot. That matters for larger codebases, where the logging convention should be a project-wide thing rather than a property of the web framework. The configuration has a required mode, which controls where server logging goes, with valid modes stderr-terminal and file, and a required level, with trace, debug, info, warn, error and critical as an increasing order of severity, where messages at the specified level and more severe are included. In file mode there are two more required properties, path for the destination file and if_exists for what to do when the file is already there, with the three valid values being append, truncate, and fail, where fail causes the server to exit immediately with an error. That last option is a small but well-chosen behaviour for a service that should not start with a confusing log, and the stderr-terminal mode produces human-readable output with colours and terminal formatting if possible, while file mode writes Bunyan-format output.
The design bet: there is no every-request handler
The most valuable part of this README is a section titled why is there no way to add an API handler function that runs on every request, and it is worth reading the argument in full because it explains the crate's character. Many web frameworks, in Rust or another language, let you specify a chain of handlers for each route, and you can usually add handlers that run before or after every request regardless of the route. The Dropshot authors found that after years of evolving a complex API server this way, the control flow for a particular request becomes hard to follow and the implicit dependencies between handlers in the chain become hard to understand, which made working on those servers time-consuming and error-prone. Their alternative is to rely on ordinary function calls instead. If the purpose of those handlers is to share code, then the shared code should be a function that returns a typed value, and the big risk becomes that someone forgets an important call, such as the one that authenticates or authorizes a user. The proposed pattern is utility functions returning typed values, with an authentication function returning an authentication context struct, which anything needing authentication consumes as a function argument. An author of a handler then knows whether they have a context available and, if not, how to get one, and the composition is explicit because an authorization function that needs an authenticated context can take it as an argument and return its own context. The project is candid that it is early and may need richer facilities, and it invites feedback.
Running the tests, the formatter and a strict lint policy
The contributor instructions are short and unusually specific about the gates. The whole test suite builds and runs with cargo test, and the README states that the suite runs cleanly and should remain clean. Formatting is cargo fmt, and CI checks that the code is correctly formatted. Clippy is used to check for common code issues, with most style checks disabled as configured in the workspace lints section of Cargo.toml, and you can run it with cargo clippy --all-targets -- --deny warnings, and CI runs clippy as well. The lint configuration in the manifest is where the project's real preferences show. The style group is set to allow with a negative priority, on the grounds that style nits are useful but not worth keeping in CI, while the disallowed_ namespace of lints, covering macros, methods, names, script identifiers and types, stays at warn, and a set of relatively stable iteration lints such as iter_cloned_collect, iter_next_slice and len_zero are enabled for the same reason. There is also a longer comment about declare_interior_mutable_const, explaining that it can identify real bugs where a const Atomic is used like a static Atomic, that it is subject to false positives, that the lint is kept to catch the former, and that it notes a Clippy issue about const values with a type containing bytes::Bytes.
Where Dropshot is the wrong tool
Four limits are clear from the repository itself. The first is the handler chain question, and it is the one to think about hardest. If your architecture needs something that runs around every request, a cross-cutting concern such as request tracing, a tenant lookup, a rate limit or a feature flag, then you are signing up to implement it as a typed context that every handler must thread through by hand, and the framework will not catch a handler that forgets to. The project argues this is a feature, and it may well be for a team that has been burned by implicit chains, but it is a real change in how the code is written. The second is that this is a REST API crate, not a full web framework. There is no template rendering, no static file serving, no session middleware, and nothing about websockets in the documentation, so a project that also needs to serve an HTML front end is looking at a second stack. The third is the version floor, with edition 2024 and rust-version 1.88, which is recent enough that a team pinned to an older toolchain cannot use it without work. The fourth is that the crate comes from a company that builds a specific appliance, so the API surface and the configuration vocabulary are shaped by that context, and the README's own tone, with its design notes and its issue references, suggests a project with a point of view rather than a neutral utility.
Editorial conclusion
Adopt Dropshot if you are building an API server in Rust and want typed handler registration, a documented configuration format and a tool that assumes an API is a public contract rather than a set of endpoints. Do not adopt it if your design depends on middleware that runs around every request, because the crate deliberately has no equivalent, and the workaround the project proposes is a pattern you write yourself. Before you commit, read the design notes on handler chains and issue 58, and note the cost side: the crate pins a recent Rust edition and version, and the repository publishes no GitHub releases, so versioning comes from the crates.io release and the CHANGELOG.adoc rather than from tags you can see here.
Frequently asked questions
What is Dropshot?
It is a general-purpose crate for exposing REST APIs from a Rust program, developed by Oxide Computer and licensed under Apache-2.0. The documentation is published on docs.rs/dropshot.
Why does Dropshot not allow a handler that runs on every request?
The README explains that handler chains made control flow hard to follow and dependencies implicit over years of use, so the project relies on function calls instead: utility functions return typed context values that handlers consume as arguments, so a forgotten authentication call is visible in the signature.
How is a Dropshot server configured?
With a TOML file. The documented properties include bind_address, default_request_body_max_bytes with a default of 1024 that can be overridden per endpoint, and the tls section with a type of AsFile or AsBytes plus the matching certificate and key settings.
What are the two packages in the Dropshot workspace?
The dropshot crate, which consumers need to know about, and dropshot_endpoint, a procedural macro used for registering API handler functions. The workspace uses resolver 2, edition 2024 and rust-version 1.88.
How do I run the Dropshot tests and lints?
Run `cargo test` for the whole suite, `cargo fmt` for formatting which CI checks, and `cargo clippy --all-targets -- --deny warnings` for the lints, which CI also runs. To run an example, use `cargo run --example basic` from the repository.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/oxidecomputer-dropshot)Was this useful?