graphql-rust/juniper: A Code-First GraphQL Server Library for Rust
GraphQL server library for Rust
At a glance
- What is it?
- Juniper defines GraphQL schemas directly from Rust types and leaves the HTTP layer to you. The trade-off is a pre-1.0 API, non-null-by-default type mapping, and no built-in server.
- Who is it for?
- Juniper fits Rust teams that already run actix, axum, hyper, rocket or warp and want schema types checked by the compiler instead of generated at runtime. It does not fit teams that want a batteries-included server or a frozen API surface.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 1 day 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 September 30, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What Juniper Solves for Rust Backends
GraphQL servers in most languages define the schema in a separate document or in strings, then check queries against it at runtime. Juniper takes the opposite route. The README describes a "code-first approach to defining GraphQL schemas", where Rust structs, enums and traits become GraphQL types through the codegen crate. A field of type Vec<Episode> becomes [Episode!]! in the schema. To express a nullable list of nullable episodes you write Option<Vec<Option<Episode>>>. That mapping is the core design decision, and it is the one most likely to surprise a developer coming from JavaScript GraphQL tooling, where nullability is opt-in.
The audience is narrow and specific. If your service is already a Rust binary serving HTTP through actix, axum, hyper, rocket or warp, Juniper adds the GraphQL execution layer without asking you to hand over your server. The README states plainly that Juniper "does not include a web server - instead it provides building blocks to make integration with existing servers straightforward". Teams that want a single framework to own routing, middleware and GraphQL will find that split inconvenient rather than liberating.
How Schema Execution and Integrations Fit Together
The repository is a Cargo workspace, not one crate. The root Cargo.toml lists members including juniper, juniper_codegen, juniper_subscriptions, juniper_graphql_ws, and one crate per framework: juniper_hyper, juniper_rocket, juniper_warp, juniper_actix, juniper_axum. The core crate carries the schema, parser, validation and execution logic; the codegen crate provides the derive macros; the framework crates adapt a Juniper schema to that framework's request and response types.
Execution has two entry points. The README states that Juniper "supports both asynchronous and synchronous execution using execute() and execute_sync() respectively", and that asynchronous execution is runtime agnostic. That means the core does not require Tokio, even though the integration crates sit on top of frameworks that do. Serialization format and network transport are also left open, so the same schema can be driven from a test harness or from an HTTP handler.
Schema coverage follows the GraphQL specification of October 2021, including interfaces, unions, schema introspection and validation. Juniper can also print the schema in GraphQL Schema Language. The subscriptions side lives in separate crates, juniper_subscriptions and juniper_graphql_ws, which is a sensible separation: most services never need them, and pulling them into the core would force a transport choice on everyone.
Installing Juniper and Running a First Query
The README does not print install commands. It points to crates.io for the package and to the Juniper Book for guides, with the Quickstart section named as the fast path. The crate name on crates.io is juniper, and the latest release listed for it is 0.17.1, published on 2026-01-26.
If you are wiring the schema into a web framework, you add the matching integration crate as well. The workspace contains one for each supported framework, and the release list shows juniper_warp at 0.9.0 and juniper_subscriptions at 0.18.0, both published on 2025-09-08. Note that the version numbers of the integration crates do not track the core crate, so check each one individually rather than assuming they share a version.
The book is the actual documentation surface. The README calls it "very much WIP" and offers a master-branch build alongside the current one, plus a book index for versions published after 0.11.1. That versioned book is the detail worth noticing: if you pin an older Juniper, you should read the book revision that matches it, because the macros and context types have moved between releases. For the schema shape itself, the repository ships the Star Wars test schema at juniper/src/tests/fixtures/starwars/schema.rs, which the README recommends as a complex example covering polymorphism with traits and interfaces. Per-framework example folders exist for actix, axum, hyper, rocket and warp, and the README links the Quickstart section of the book as the shortest path to a running schema.
Where Juniper Gets in the Way
The README's API Stability section is unusually direct: "Juniper has not reached 1.0 yet, thus some API instability should be expected." For a library you plan to keep for years, that is the single largest cost. Upgrades between minor versions can require code changes, and the split between the core crate, the codegen crate and the framework crates multiplies the number of version pairs you have to keep consistent.
The absence of a web server is a second constraint that is easy to underestimate. Juniper gives you execution, not an endpoint. You still choose a framework, write the handler, decide how to read the request body, and decide what to do with errors before they reach the client. The integration crates reduce that work but do not remove the decision. If your team has no strong preference among the five supported frameworks, Juniper hands you a choice you may not want to make.
The non-null-by-default mapping is a third friction point. It is coherent, and it matches how most Rust APIs are actually written, but it inverts the habit of developers who learned GraphQL elsewhere. A field that can be absent has to be wrapped in Option, and a nullable element inside a list needs a second Option. Getting that wrong changes the published schema in a way that clients will notice. The README states the mapping explicitly, which suggests the maintainers know it is a common source of confusion.
Finally, the book is described by the README as WIP. Guides that lag behind the code are a real cost when the codegen macros are the main thing a new user has to learn.
Juniper Against Async-GraphQL and Schema-First Generation
The obvious comparison is async-graphql, which also derives a GraphQL schema from Rust types but ships its own HTTP integration story rather than delegating entirely to the web framework. The practical difference is scope: Juniper keeps execution separate from transport and offers per-framework crates, while async-graphql bundles more of the server path. If you want to plug GraphQL into an existing actix or axum service with minimal new concepts, that bundling is a feature. If you want the execution engine to be testable without any HTTP layer, Juniper's separation is the better fit.
The second alternative comes from inside the Juniper ecosystem. The README points to juniper-from-schema for teams that prefer a schema-first workflow, generating Rust code from a .graphql schema file instead of deriving the schema from Rust types. That is a genuine fork in approach, not a wrapper: with a schema file as the source of truth, the schema can be reviewed and versioned independently of the Rust code, and codegen failures surface at build time against a document rather than against type definitions. The cost is a generation step and a less direct mapping between a Rust field and its GraphQL counterpart. Juniper itself stays code-first, and the README is clear that schema-first is the other project's job.
A third option is not using a GraphQL library at all and hand-writing resolvers over a REST or RPC layer. That is the right call when your clients are few and known, because GraphQL's value comes from letting unknown clients shape queries.
Releases, Licence and Upgrade Cost
The repository was last pushed on 2026-09-03 and is not archived, so the codebase is being touched. That says nothing about the API surface, which the README explicitly warns is unstable before 1.0. Release cadence is visible in the release list: juniper 0.17.1 landed on 2026-01-26, while juniper_warp 0.9.0 and juniper_subscriptions 0.18.0 both landed on 2025-09-08. The gap between the core crate and the integration crates is the practical upgrade problem. When you bump juniper, you should check the release notes for each juniper_* crate you depend on rather than assuming they moved together.
The Makefile shows the maintainers' own workflow, and it is worth reading before you file an issue. Linting runs cargo clippy --workspace --all-features with -D warnings, formatting requires a nightly toolchain via cargo +nightly fmt --all, and releases go through cargo-release with per-crate version bumps. The presence of a RELEASING.md and a release.toml at the repository root confirms that releases are a deliberate, scripted process rather than ad hoc tags.
On licensing, the repository carries a LICENSE file and GitHub reports the licence as NOASSERTION, meaning the platform could not match it to a known identifier. The README does not discuss licence terms. If you are distributing a product that links Juniper, read the LICENSE file in the repository and get your own answer; nothing in the README substitutes for that.
Editorial conclusion
Juniper fits Rust teams that already run actix, axum, hyper, rocket or warp and want schema types checked by the compiler instead of generated at runtime. It does not fit teams that want a batteries-included server or a frozen API surface. Before adopting, read the API Stability note in the README, check which juniper-* crate version matches your web framework, and confirm on docs.rs whether the 0.17 codegen output matches the book pages you are following.
Frequently asked questions
Does Juniper include a web server?
No. The README states that Juniper does not include a web server and instead provides building blocks for integration with existing servers. It optionally ships pre-built integrations for actix, axum, hyper, rocket and warp.
Which web frameworks does Juniper integrate with?
The README lists actix, axum, hyper, rocket and warp, each with its own crate in the workspace and its own examples folder. Embedded GraphiQL and GraphQL Playground are included for debugging.
Is the Juniper API stable?
No. The README's API Stability section says Juniper has not reached 1.0 and that some API instability should be expected. The latest release listed for the core crate is juniper 0.17.1.
Can Juniper generate code from a GraphQL schema file instead?
Not by itself. Juniper follows a code-first approach, and the README points to juniper-from-schema for teams that want a schema-first workflow.
Does Juniper support subscriptions?
Subscriptions live in separate crates, juniper_subscriptions and juniper_graphql_ws, rather than in the core crate. The latest listed release for juniper_subscriptions is 0.18.0.
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/graphql-rust-juniper)