nickel-org/nickel.rs
An expressjs inspired web framework for Rust
Healthy across all four use cases
weakest axisPermissive license, no critical CVEs, actively maintained — safe to depend on.
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓25+ active contributors
- ✓Distributed ownership (top contributor 40% of recent commits)
- ✓MIT licensed
Show all 6 evidence items →Show less
- ✓CI configured
- ✓Tests present
- ⚠Stale — last commit 4y ago
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Healthy" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/nickel-org/nickel.rs)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/nickel-org/nickel.rs on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: nickel-org/nickel.rs
Generated by RepoPilot · 2026-05-09 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/nickel-org/nickel.rs shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
GO — Healthy across all four use cases
- 25+ active contributors
- Distributed ownership (top contributor 40% of recent commits)
- MIT licensed
- CI configured
- Tests present
- ⚠ Stale — last commit 4y ago
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live nickel-org/nickel.rs
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/nickel-org/nickel.rs.
What it runs against: a local clone of nickel-org/nickel.rs — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in nickel-org/nickel.rs | Confirms the artifact applies here, not a fork |
| 2 | License is still MIT | Catches relicense before you depend on it |
| 3 | Default branch master exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 1489 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of nickel-org/nickel.rs. If you don't
# have one yet, run these first:
#
# git clone https://github.com/nickel-org/nickel.rs.git
# cd nickel.rs
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of nickel-org/nickel.rs and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "nickel-org/nickel.rs(\\.git)?\\b" \\
&& ok "origin remote is nickel-org/nickel.rs" \\
|| miss "origin remote is not nickel-org/nickel.rs (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
&& ok "license is MIT" \\
|| miss "license drift — was MIT at generation time"
# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
&& ok "default branch master exists" \\
|| miss "default branch master no longer exists"
# 4. Critical files exist
test -f "src/lib.rs" \\
&& ok "src/lib.rs" \\
|| miss "missing critical file: src/lib.rs"
test -f "src/nickel.rs" \\
&& ok "src/nickel.rs" \\
|| miss "missing critical file: src/nickel.rs"
test -f "src/router/router.rs" \\
&& ok "src/router/router.rs" \\
|| miss "missing critical file: src/router/router.rs"
test -f "src/middleware.rs" \\
&& ok "src/middleware.rs" \\
|| miss "missing critical file: src/middleware.rs"
test -f "src/request.rs" \\
&& ok "src/request.rs" \\
|| miss "missing critical file: src/request.rs"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 1489 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~1459d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/nickel-org/nickel.rs"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
Nickel.rs is an Express.js-inspired web framework for Rust that uses hyper 0.14 (async) for handling HTTP requests. It provides a lightweight, ergonomic API for building web applications with middleware support, routing, request/response parsing, and built-in features like static file serving, CORS, templating via Mustache, and JSON serialization. Single-crate structure: src/lib.rs is the entry point; src/extensions/ holds request/response trait extensions; src/macros/ provides router!() and middleware!() DSL macros; examples/ contains 20+ runnable scenarios (hello_world.rs, routing.rs, json.rs, https.rs, etc.); assets/ holds templates and test fixtures.
👥Who it's for
Rust developers building HTTP-based web services who want Express-like syntactic familiarity and a batteries-included framework without heavyweight complexity. Contributors are web framework enthusiasts and Rust async runtime practitioners.
🌱Maturity & risk
Production-ready but moderately active: the project transitioned from 0.11.x (hyper 0.10 synchronous) to 0.12.x (hyper 0.14 async) on the master branch, CI is configured (Travis/AppVeyor), but commit frequency and issue response suggest it's not heavily maintained. Version 0.13.x is planned as a potential 1.0.
Moderate risk: depends on 16+ external crates including tokio and hyper (both stable but large); no recent version bump visible in the snippet (0.12.1 is current); the comment in Cargo.toml notes some examples are broken ('TODO: needs updated middleware and router macros'); single/few maintainers increases bus factor. Async migration to hyper 0.14 is relatively recent, so edge cases may exist.
Active areas of work
Active development on async hyper 0.14 integration (0.12.x branch/master); maintenance of bug fixes for 0.11.x on maint-0.11.x branch; planning for 0.13.x with new features. Several examples are listed as broken and awaiting macro/middleware updates.
🚀Get running
git clone https://github.com/nickel-org/nickel.rs.git && cd nickel.rs && cargo build && cargo run --example hello_world, then visit http://127.0.0.1:6767
Daily commands: cargo run --example hello_world (or substitute any example name from Cargo.toml [[example]] sections); the server listens on 127.0.0.1:6767 by default in most examples.
🗺️Map of the codebase
src/lib.rs— Entry point and public API surface; defines what users import and understand about the framework's capabilitiessrc/nickel.rs— Core Nickel struct that orchestrates the entire server lifecycle, routing, and middleware chain executionsrc/router/router.rs— Route matching and handler dispatch logic; fundamental to how requests map to middleware and handlerssrc/middleware.rs— Middleware trait definition and execution model; defines the async callback pattern all handlers followsrc/request.rs— Request wrapper around hyper that exposes convenience methods; every handler receives this as contextsrc/response.rs— Response builder and writer; controls how handlers produce HTTP responses sent back to clientssrc/server.rs— Hyper server integration layer; manages socket binding, TLS, and async runtime coordination
🛠️How to make changes
Add a new HTTP route handler
- Open src/nickel.rs and locate the method signatures for get(), post(), etc. (
src/nickel.rs) - Call server.get("/path", middleware!(/* your handler */)) or implement Middleware trait (
src/middleware.rs) - Access request data via the Request parameter and call resp.send() or resp.send_file() (
src/request.rs) - See examples/routing.rs for route syntax patterns (regex, parameters, etc.) (
examples/routing.rs)
Add custom middleware for request processing
- Implement Middleware trait from src/middleware.rs with your handler logic (
src/middleware.rs) - Use async-trait and handle async closures; return MiddlewareResult::Continue or MiddlewareResult::Halt (
src/middleware.rs) - Register middleware globally with server.utilize() or per-route via chaining (
src/nickel.rs) - Check examples/logger_middleware.rs and examples/enable_cors.rs for implementation patterns (
examples/logger_middleware.rs)
Parse JSON or form data from request body
- The body_parser module automatically parses JSON and form-encoded bodies on Request (
src/body_parser.rs) - Use req.json_as::<T>() for JSON or req.form_data() for URL-encoded forms in your handler (
src/request.rs) - Define a serde-compatible struct for deserialization targets (
examples/json.rs) - Handle the Result; errors are automatically wrapped and sent to error handler (
src/default_error_handler.rs)
Serve static files or assets
- Import StaticFilesHandler from src/static_files_handler.rs (
src/static_files_handler.rs) - Call server.get("public/**", StaticFilesHandler::new("examples/assets")) in src/nickel.rs (
src/nickel.rs) - Place assets in the directory path; wildcard routes match the file tree hierarchy (
examples/static_files.rs) - MIME types are inferred via src/mimes.rs based on file extension (
src/mimes.rs)
🔧Why these technologies
- Hyper 0.14 — Low-level async HTTP runtime with full HTTP/1.1 support; provides socket management, TLS via rustls, and integrates seamlessly with Tokio
- async-trait — Allows trait methods to be async without requiring a nightly compiler; essential for the Middleware trait pattern that all handlers implement
- Regex routing — Enables flexible path patterns (parameters, wildcards, regex) similar to Express.js; compiled once at startup via lazy_static
- Serde (via body_parser) — JSON deserialization/serialization; allows automatic type-safe parsing of request bodies into user-defined structs
⚖️Trade-offs already made
-
Express.js-inspired API over idiomatic Rust patterns
- Why: Lower barrier to entry for JavaScript developers; familiar route syntax and middleware chaining model
- Consequence: Some Rust idioms (like error handling via Result) are wrapped; less type safety at framework edges
-
Synchronous route registration (server.get(), .post()) over dynamic registration
- Why: Simple, predictable startup; all routes known before server binds
- Consequence: Cannot dynamically add routes at runtime; must restart server for route changes
-
Single-threaded routing with async handlers vs. multi-threaded worker pool
- Why: Simpler model; Tokio runtime handles I/O multiplexing and thread spawning internally
- Consequence: CPU-bound handlers may block the event loop; users must use separate tasks for heavy computation
-
Wildcard and regex route matching over trie-based routing
- Why: Flexibility and simplicity; Express-like developer experience
- Consequence: Route matching is O(n) in number of routes; scales poorly with hundreds of endpoints
🚫Non-goals (don't propose these)
- Does not provide built-in authentication or session management (users implement via middleware)
- Does not include ORM or database abstraction (users choose their own database driver)
- Does not provide request validation framework (form validation is delegated to serde or manual checks)
- Does not handle request/response streaming natively (body is fully buffered before handler runs)
- Does not include GraphQL, WebSockets, or Server-Sent Events support (HTTP request/response only)
🪤Traps & gotchas
Macros are versioned with the framework—some example macros are broken and noted in Cargo.toml (macro_example.rs is commented out); async requires tokio runtime setup not always obvious in simple examples; template file paths in examples/ are relative and sensitive to working directory; HTTPS examples (https.rs) require key.pem and self_signed.crt in examples/assets/; the modifier pattern (src/modifier) is not well-documented in examples and can be confusing for newcomers.
🏗️Architecture
💡Concepts to learn
- Middleware pattern — Core to nickel.rs API—middleware!() macros and chaining define how requests flow through handlers; understanding this is essential to writing any nickel app
- Tokio async runtime — Nickel 0.12.x depends on Tokio 1 for event-driven concurrency; necessary to understand backpressure, task scheduling, and why .listen() blocks
- Hyper connection pooling and framing — Hyper 0.14 handles HTTP/1.1 keep-alive and chunked encoding transparently; nickel.rs users interact with this indirectly but edge cases (large uploads, streaming) expose it
- Modifier trait pattern — Nickel uses the 'modifier' crate to allow composable, chainable modifications to responses (e.g. setting headers, status); less common in modern Rust but core to nickel's ergonomics
- Regex-based route matching — Routes like /users/:id or /api/** are compiled to regex; understanding named groups and backtracking is useful for performance and correctness of complex route patterns
- Typemap for request-scoped state — Nickel stores per-request context (authenticated user, database connection, etc.) in a typemap; essential pattern for middleware coordination
- Server-Sent Events (SSE) and WebSocket upgrade — Not explicitly shown in examples but hyper 0.14 supports Upgrade header and response streaming; nickel.rs can handle these but requires raw hyper knowledge
🔗Related repos
actix/actix-web— Primary Rust web framework alternative; also async, higher performance, more widely adopted than nickel.rstokio-rs/axum— Modern Tokio-native web framework with stronger async/await ergonomics; represents the current Rust web directionserde-rs/serde— Serialization ecosystem nickel.rs depends on; nearly universal in Rust web frameworkshyperium/hyper— Underlying HTTP library (hyper 0.14); nickel.rs is a high-level wrapper around itbrynary/rack— Spiritual predecessor in Ruby; inspired the middleware/routing patterns Express and thus nickel.rs adopted
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add comprehensive unit tests for src/router/matcher.rs and src/router/into_matcher.rs
The router matching logic is critical to the framework but there's no dedicated test file for the matcher module. This is foundational code that deserves thorough coverage. New contributors can add tests for route pattern matching, regex routes, parameter extraction, and edge cases that would catch regressions early.
- [ ] Create tests/router_matcher_tests.rs with test cases for src/router/matcher.rs
- [ ] Add test cases for parameter extraction (e.g., /user/:id/:name patterns)
- [ ] Add test cases for regex routes based on examples/regex_route.rs
- [ ] Add test cases for wildcard matching (**) based on examples/hello_world.rs
- [ ] Add edge case tests (empty patterns, special characters, overlapping routes)
- [ ] Verify tests pass with
cargo test --test router_matcher_tests
Migrate CI from Travis CI and AppVeyor to GitHub Actions
The repo still uses .travis.yml and .appveyor.yml (legacy CI platforms). GitHub Actions is now the standard and provides better integration. This involves creating a modern workflow that tests against stable/nightly Rust, runs on multiple platforms (Linux/Windows/macOS), and validates examples.
- [ ] Create .github/workflows/rust.yml with matrix strategy for stable/nightly Rust versions
- [ ] Add matrix for os: [ubuntu-latest, windows-latest, macos-latest]
- [ ] Include steps for
cargo test,cargo test --all-examples, and clippy lints - [ ] Test that all examples in examples/ directory compile and run successfully
- [ ] Remove .travis.yml and .appveyor.yml after verification
- [ ] Update README.md badge to point to GitHub Actions workflow status
Add integration tests for middleware execution order and custom error handlers
examples/custom_error_handler.rs exists but there are no dedicated integration tests validating middleware chain execution, error propagation, and custom error handler behavior. This is critical for ensuring the middleware system works as expected across versions.
- [ ] Create tests/middleware_chain_tests.rs for testing middleware execution order
- [ ] Add tests that verify multiple middleware execute in correct sequence
- [ ] Add tests for error handler invocation when middleware returns errors
- [ ] Add tests for the custom_error_handler example flow (request → error → handler response)
- [ ] Add tests for middleware that short-circuit the chain
- [ ] Add tests for request/response modifications by middleware in src/extensions/request.rs and src/extensions/response.rs
- [ ] Verify tests pass with
cargo test --test middleware_chain_tests
🌿Good first issues
- Add integration tests for the router!() and middleware!() macros in examples/macro_example.rs and examples/no_macro_custom_data.rs—they are currently disabled with TODO comments in Cargo.toml
- Write doc comments for src/extensions/request.rs and src/extensions/response.rs trait methods; most lack examples and are only 1-2 lines of explanation
- Create a comprehensive example combining form_data (examples/form_data/form_data.rs), JSON responses, and error handling in a single realistic scenario (e.g. a contact form with validation)
⭐Top contributors
Click to expand
Top contributors
- @jolhoeft — 40 commits
- @Ryman — 30 commits
- @Vaelden — 4 commits
- @robertohuertasm — 2 commits
- @cburgdorf — 2 commits
📝Recent commits
Click to expand
Recent commits
82deeee— chore(*): update dependencies and Rust edition (#462) (damfle)bda1360— Repair tests for async (#461) (jolhoeft)7d12245— chore(*): Upgrade to hyper-0.14.x (#459) (jolhoeft)f1388af— Merge pull request #454 from SuliacLEGUILLOU/master (jolhoeft)af8ccfa— docs(MW): Demonstrate mw stack data sharing (suliacLEGUILLOU)da7770a— Merge pull request #447 from jolhoeft/dev (jolhoeft)0f1d55d— Add missing routing methods for #444 (jolhoeft)b8fe4f4— Merge pull request #377 from dtolnay/map (jolhoeft)43c0b1f— Merge pull request #439 from danielorf/jsonexamplecomments (jolhoeft)e5ab9b8— fix(*): curl routes in examples/json.rs (danielorf)
🔒Security observations
The nickel.rs web framework has a moderate security posture with several
- High · Outdated Hyper Dependency —
Cargo.toml - hyper = { version = "0.14", features = ["full"] }. The codebase uses hyper 0.14, which is several major versions behind the current release (0.15+). Hyper 0.14 may contain known security vulnerabilities that have been patched in newer versions. The dependency has reached end-of-life status. Fix: Update to the latest stable version of hyper (currently 1.x or latest 0.15.x) and test compatibility. Review hyper's security advisories at https://rustsec.org/ - High · Outdated Tokio Dependency —
Cargo.toml - tokio = { version = "1", features = ["full"] }. The codebase uses tokio 1.x with full features enabled. While tokio 1 is still maintained, the full feature set includes unnecessary components that increase the attack surface. Additionally, ensure the specific patch version addresses known vulnerabilities. Fix: Review and minimize tokio features to only required components (e.g., 'macros', 'rt-multi-thread', 'net'). Keep tokio updated to the latest patch version. Check rustsec.org for tokio vulnerabilities. - Medium · Hardcoded Test Credentials in Repository —
examples/assets/key.pem, examples/assets/self_signed.crt. SSL/TLS certificates and keys are present in the examples directory (examples/assets/key.pem, examples/assets/self_signed.crt). While these are self-signed test certificates, storing private keys in version control is a security anti-pattern and could establish bad practices. Fix: Move sensitive test assets to a separate location or generate them dynamically during tests. Document the process for developers. Consider using let's encrypt or temporary certificate generation for examples. - Medium · Missing Security Headers Configuration —
src/response.rs, src/nickel.rs. The framework provides examples but does not show default security headers (Content-Security-Policy, X-Frame-Options, X-Content-Type-Options, Strict-Transport-Security). Applications built with nickel may be vulnerable to common web attacks if developers don't explicitly add these headers. Fix: Add default security headers middleware or provide clear documentation and examples of security header configuration. Consider implementing OWASP recommended headers by default. - Medium · Edition 2021 with Legacy Dependencies —
Cargo.toml - edition = "2021". While using Rust edition 2021 is positive, several dependencies are legacy versions that may not be fully compatible with modern security standards. The combination of outdated hyper (0.14) with edition 2021 creates potential incompatibilities. Fix: Perform a comprehensive dependency audit using 'cargo audit' and 'cargo outdated'. Create a migration plan to update all dependencies to their latest stable versions. - Low · Verbose Feature Flags —
Cargo.toml - hyper/full, tokio/full features. The 'full' feature flag for hyper and tokio enables all features including potentially unnecessary ones, increasing the dependency footprint and potential attack surface. Fix: Use selective feature flags instead of 'full'. Only enable features that are actually required by the application (e.g., 'http1', 'http2', 'runtime' instead of full). - Low · Missing SECURITY.md Policy —
Repository root. No security policy or vulnerability disclosure process is documented in the repository. Users and security researchers lack guidance on reporting vulnerabilities responsibly. Fix: Create a SECURITY.md file documenting vulnerability reporting procedures, supported versions, and security contact information. - Low · Audit Trail Not Present —
.travis.yml, .appveyor.yml. No evidence of regular security audits or dependency scanning. The project lacks visible security practices such as dependabot, security workflows, or audit logs. Fix: Enable GitHub's dependabot for automated dependency updates. Add cargo-audit to CI/CD pipeline. Consider regular security audits from external auditors.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.