Paperclip generates Rust HTTP APIs from OpenAPI specs, and shows you the code
WIP OpenAPI tooling for Rust.
At a glance
- What is it?
- Paperclip is a work-in-progress Rust library for OpenAPI tooling that generates server, client and CLI code through a Cargo build script rather than a procedural macro. The project says it is not ready for production use, so the honest read is that it is worth evaluating for compile-time typed APIs with a spec you control.
- Who is it for?
- Paperclip fits a team that already maintains an OpenAPI document and wants Rust server, client and CLI bindings whose generated source can be read, diffed and debugged, and that is willing to work with a library whose own README says it may not be ready for production use. It does not fit a team that needs a stable 1.0 dependency today, one that wants a spec validator and host rather than a code generator, or one on a stack other than the actix-web plugin.
- 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?
- Activity is slowing. The repository last received commits 6 months 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 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What Paperclip is meant to become once it is finished
Paperclip is tooling around the OpenAPI specification for Rust. The project describes itself as work in progress, and the README is explicit about the status: it is under development and may not be ready for production use just yet. That sentence, more than anything else, should shape your evaluation.
The intended end state has three parts. First, code generation for type-safe, compile-time checked HTTP APIs in Rust, covering a server, a client and a CLI. Second, support for processing, validating and hosting an OpenAPI specification. Third, customization of both the spec and the generated code. The first part is what the repository layout actually shows today, with a build script, a set of plugin crates and a test suite that compiles generated code; the second and third are stated as goals rather than demonstrated by anything in the repository description.
The audience is a Rust developer who has an API contract, probably already written as an OpenAPI document, and wants the Rust side of that contract to be typed rather than hand-maintained. The value proposition is that the spec becomes the single source of truth and the compiler becomes the check on it.
Build scripts and include!, because generated code has to be readable
The most interesting design decision in Paperclip is the one the project answers at length in its own FAQ. The question is why it generates raw Rust code instead of using procedural macros for compile-time generation. The answer given is that procedural macros are not the right approach for REST APIs, because with a macro you sorta have to guess what the generated code does. The author wants to be able to see the generated code to identify names, fields and supported methods.
That is a genuine trade-off, and the alternative it points to is instructive. Compile-time generation is still available, but through build scripts instead. A build script writes the code to disk and the project includes it with the include! macro.
build = "src/build/build.rs"The same answer notes that procedural macros are still used elsewhere in the project, in the macros directory, so the rejection is specific to how the API surface is produced.
The cost of this choice is visible too. Generated code on disk means the crate carries a build step, the output has to be gitignored or committed deliberately, and a stale generated file is a real failure mode. The benefit is that a code review can read the whole API surface, and that a compiler error can be traced to a line you can actually open.
The workspace is split into core, macros, plugins and cli-ng, and the versions drift
The root Cargo.toml describes the paperclip package at version 0.9.7 on edition 2018, and the internal crates are path dependencies inside the same repository.
paperclip-actix = { path = "plugins/actix-web", version = "0.7.4", optional = true }
paperclip-core = { path = "core", version = "0.7.4" }
paperclip-macros = { path = "macros", version = "0.7.1", optional = true }
paperclip-ng = { path = "cli-ng", version = "0.1.3", optional = true }The version numbers deserve a second look. The root crate is 0.9.7, paperclip-core and paperclip-actix sit at 0.7.4, paperclip-macros at 0.7.1 and paperclip-ng at 0.1.3. The components do not move in lockstep with the published root version, which is worth knowing if you pin exact versions or follow the crates.io releases rather than the git repository.
There is also a command line binary in the package.
[[bin]]
name = "paperclip"
path = "src/bin/main.rs"
required-features = ["cli"]The required-features field means the binary is only built when the cli feature is on, so a plain dependency on the library does not pull in the command line tool. The optional dependencies line up with that: the actix plugin, the macros, the ng CLI and the swagger-ui related crates are all feature gated rather than always compiled.
Local setup: clone with submodules, then make prepare
The project documents its own development setup, and it is specific enough to follow without guessing. You clone the repository along with its submodules, make sure rustup is installed, move into the directory, and run the setup routine.
git clone --recurse-submodules [email protected]:paperclip-rs/paperclip.gitcd paperclipThe setup target sets the toolchain override to stable, installs rustfmt and clippy for the active toolchain, installs a nightly toolchain with the same components, and initialises the submodules recursively.
make prepareThe submodules matter. The repository carries a .gitmodules file, and the test fixtures in tests/test_k8s and tests/test_pet are checked out separately, so a shallow clone without them will not build. The stable toolchain is what compiles the crate, while nightly is used for the formatting and lint passes.
After the setup, a single command builds the project and runs the tests.
makeThe Makefile default target chains check, build and test, so that one command formats, lints, compiles and tests. If you only want the library, cargo build --all is the underlying step.
The Makefile feature list decides which plugins get compiled
Almost every build and test command in this repository is qualified by an explicit feature list, and the lists differ from target to target. The build step compiles everything, then compiles again with a larger set.
cargo build --all
cargo build --all --features "actix4 cli cli-ng chrono url uuid swagger-ui v3"The test step uses yet another combination, and the Makefile carries a comment explaining a special case: one test is run separately because it affects the generated spec, which would otherwise fail the other tests.
cargo test --all --features "actix4 cli chrono uuid swagger-ui rapidoc actix4-validator"From these lists you can read the surface of the project. There is an actix4 integration, a cli and a cli-ng, optional support for chrono, url and uuid types, a swagger-ui bundle, rapidoc as an alternative documentation view, a v3 flag for the specification version, and validators for actix4. The lint target runs clippy with the same broad feature set and denies clippy::all while allowing derivable-impls, which tells you the code is held to a strict lint bar.
For a user, the practical consequence is that the features you enable change what is compiled and what is generated, so the feature list is part of the contract rather than a build detail.
Generated code is compiled during the test suite, not just inspected
The most reassuring line in the Makefile is the one that compiles the output of the generator. The test target finishes by running cargo check inside the generated projects.
cd tests/test_pet && cargo check
cd tests/test_pet/cli && CARGO_TARGET_DIR=../target cargo check
cd tests/test_k8s && cargo check
cd tests/test_k8s/cli && CARGO_TARGET_DIR=../target cargo checkTwo things follow from this. First, a change that breaks the generator breaks the build, because the broken output no longer compiles. Second, the generated CLI projects are compiled too, with a shared target directory so the two checks reuse artifacts.
The fixtures themselves live in separate directories, test_pet and test_k8s, and the clean target removes the generated tests/test_k8s and tests/test_pet directories and restores test_k8s from git, which tells you these directories are partly generated and partly checked in. The Makefile also keeps a commented-out line that would run the generated CLI with a help flag, so the intent to smoke test the CLI exists even if the invocation is currently disabled.
The repository top level also holds a book directory for documentation, a plugins directory for the actix-web integration, a core directory, a macros directory, a cli-ng directory and a shell.nix file for reproducible shell environments.
Known rough edges: unhelpful compile errors and an unfinished feature set
The project's own FAQ asks whether the error thrown at compile time could be more useful, and the answer is blunt: none that the author can think of, sadly. That is an honest admission that the compile-time failure mode is worse than it should be. When a generated API does not line up with your spec, the diagnostic you get is the standard Rust error pointing into generated code, not a message that names the offending operation or field.
The second rough edge is scope. The README frames the whole project as work in progress, and the spec processing, validation and hosting capabilities are described as things it will provide once complete. Anyone adopting this now is adopting a code generator with a plausible future, not a finished OpenAPI platform.
A third constraint is the plugin story. The actix-web integration is the one visible in the dependency list, which means the server side is tied to that framework. If your Rust service runs on a different HTTP stack, the parts of Paperclip you can use shrink to the core, the macros and the CLI generation, and the server side is not covered by what this repository shows.
There is also a stated interest in new ideas. The README says new ideas are needed, which reads as an invitation to contribute rather than a roadmap, and it pairs that with a Discord server and the Rust Code of Conduct.
Licence, release cadence and the gap since the last push
Paperclip is dual licensed. The README says you may use it under either the Apache License, Version 2.0 or the MIT license, at your option, and the root Cargo.toml records the same choice as MIT OR Apache-2.0. The repository carries both LICENSE-APACHE and LICENSE-MIT files. That choice is generous for adoption, since an organisation with a house licence policy can pick the one that fits without changing the dependency.
Releases have been infrequent rather than regular. v0.9.5 was tagged on 2025-03-10, v0.9.6 on 2025-06-18 and v0.9.7 on 2026-04-20. The last push to the repository was on 2026-04-20 as well, which tells you the newest tag and the most recent code movement are the same event rather than a tag followed by quiet work.
The 0.x version numbers are the more important signal. A pre-1.0 crate carries no compatibility promise, and with a generator whose output you will commit to your repository, a change in generated code between versions is a migration, not a patch upgrade. Plan to review the diff of generated sources when you move between releases rather than bumping the dependency and rebuilding.
The project also lists sponsors, keeps a CHANGELOG.md, and follows the Rust Code of Conduct, which is a reasonable sign of an ordinary community-maintained crate.
Editorial conclusion
Paperclip fits a team that already maintains an OpenAPI document and wants Rust server, client and CLI bindings whose generated source can be read, diffed and debugged, and that is willing to work with a library whose own README says it may not be ready for production use. It does not fit a team that needs a stable 1.0 dependency today, one that wants a spec validator and host rather than a code generator, or one on a stack other than the actix-web plugin. Before committing, generate one small spec with the actix4 and cli features, read the emitted Rust, and check whether the paperclip-actix plugin at version 0.7.4 matches the actix-web release you already run.
Frequently asked questions
Why does Paperclip generate raw Rust code instead of using procedural macros?
The stated reason is that generated code has to be readable so you can identify names, fields and supported methods, and with a procedural macro you sorta have to guess. Compile-time generation is still possible through build scripts that write the code and include it.
Is Paperclip ready for production use?
The project states that it is a work in progress and may not be ready for production use just yet, and it describes the spec processing, validation and hosting support as things it will provide once complete.
How do I build Paperclip from source?
Clone with git clone --recurse-submodules, install rustup, run make prepare to set up the toolchains and submodules, then run make, which chains check, build and test. The repository also ships a shell.nix for reproducible shell environments.
Which licence applies to Paperclip?
It is dual licensed under the Apache License, Version 2.0 and the MIT license, at your option, and the Cargo.toml records it as MIT OR Apache-2.0. Both LICENSE-APACHE and LICENSE-MIT files are in the repository.
Does Paperclip generate code for HTTP frameworks other than actix-web?
The dependency list shows paperclip-actix under plugins/actix-web as the only server plugin, alongside paperclip-core, paperclip-macros and paperclip-ng. The features exercised in the build include actix4, cli, cli-ng, chrono, url, uuid and swagger-ui.
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/paperclip-rs-paperclip)Was this useful?