ntex
llms.txt2.5k starsChanged 5 days agoApache-2.0View on GitHub
ntex is a Rust framework for composable network services: HTTP/1, HTTP/2, WebSocket, web applications, and custom protocols. Everything is a Service<St, Req> with a pipeline state type St, executed by a Pipeline that coordinates readiness, calls, and shutdown. Rules for generated code:
Adopted by
Steady: 0 new projects took it in the last 4 weeks, none the 4 weeks before.thin data
Who's talking about ntex-rs/ntex
- juhaku/utoipa“Initial support for ntex integration via utoipa-ntex”GitHub · Mar 26, 2025 · 3 reactions
- @matsuu.bsky.social“Rust向けのネットワークサービス用フレームワーク。golangのechoのような機能は大体揃ってそう。HTTPだけでなくgRPCやMQTTなどの実装もある模様。”Bluesky · Mar 29, 2024 · 3 likes
- A Hacker News story“Ntex – Framework for composable networking services”Hacker News · Jun 12, 2022 · 3 points
- actix/actix-web“actix-web losing race - see benchmarks, I also wonder if actix-web will be actively maintained or not?”GitHub · Aug 4, 2020 · 3 reactions
- graphql-hive/router“Subscriptions”GitHub · Dec 13, 2025 · 2 reactions
The biggest 5 of 52 mentions linking to the repository that our crawl found. Each line is their own headline.
See all 52
Talked about on GitHub, Hacker News and Bluesky (52 times).
On GitHub · 50
- Mar 26, 2025Initial support for ntex integration via utoipa-ntex — juhaku/utoipa3 reactions
- Aug 4, 2020actix-web losing race - see benchmarks, I also wonder if actix-web will be actively maintained or not? — actix/actix-websays it tested it3 reactions · 9 comments
- Dec 13, 2025Subscriptions — graphql-hive/router2 reactions · 5 comments
- Sep 17, 2024Lesson 206 - Ntex — antonputra/tutorials1 reaction · 1 comment
- Oct 30, 2022add ntex-rs — flosse/rust-web-framework-comparison1 reaction · 1 comment
45 more on GitHub
- Nov 9, 2026build(deps): bump ntex from 4.4.0 to 4.6.0 — next-hat/nanocl0 reactions
- Nov 9, 2026build(deps): bump ntex from 4.5.0 to 4.6.0 — vidhanio/hypertext0 reactions
- Nov 7, 2026build(deps): bump ntex from 4.4.0 to 4.6.0 — next-hat/metrs0 reactions
- Nov 2, 2026build(deps): bump ntex from 4.4.0 to 4.6.0 — next-hat/nanocl0 reactions · 2 comments
- Nov 2, 2026deps(cargo): bump ntex from 4.4.0 to 4.4.1 in /servers/ntex in the all-cargo-deps group across 1 directory — goceleris/probatorium0 reactions
- Oct 31, 2026build(deps): bump ntex from 4.4.0 to 4.4.1 — next-hat/metrs0 reactions · 1 comment
- Oct 27, 2026Suggest a project: ntex (auto-discovered) — rager306/awesome-blazing-rust0 reactions · 1 comment
- Oct 26, 2026deps(cargo): bump the all-cargo-deps group across 3 directories with 2 updates — goceleris/probatorium0 reactions · 1 comment
- Oct 25, 2026chore(deps): bump the ntex group across 1 directory with 8 updates — graphql-hive/router0 reactions
- Oct 24, 2026build(deps): bump ntex from 4.2.0 to 4.4.0 — next-hat/metrs0 reactions
- Oct 24, 2026build(deps): bump ntex-bytes from 1.11.0 to 1.14.0 — next-hat/metrs0 reactions
- Oct 19, 2026build(deps): bump the all-cargo group across 1 directory with 9 updates — Bleala/Docker-Proxy-Filter-DOCKERIZED0 reactions · 1 comment
- Oct 19, 2026deps(cargo): bump the all-cargo-deps group across 4 directories with 3 updates — goceleris/probatorium0 reactions · 1 comment
- Oct 17, 2026build(deps): bump ntex-bytes from 1.11.0 to 1.12.0 — next-hat/metrs0 reactions · 1 comment
- Oct 14, 2026build(deps): bump ntex from 3.12.3 to 4.3.0 — vidhanio/hypertext0 reactions · 1 comment
- Oct 14, 2026chore(deps): bump the ntex group across 1 directory with 7 updates — graphql-hive/router0 reactions · 1 comment
- Oct 12, 2026chore(deps): update ntex requirement from =3.12.3 to =4.1.0 — easy-4-rust/thymeleaf-rust0 reactions · 2 comments
- Oct 12, 2026build(deps): bump ntex from 3.12.3 to 4.1.0 — vidhanio/hypertext0 reactions · 1 comment
- Oct 12, 2026chore(deps): bump the ntex group across 1 directory with 6 updates — graphql-hive/router0 reactions · 1 comment
- Oct 7, 2026chore(deps): bump the ntex group across 1 directory with 4 updates — graphql-hive/router0 reactions · 2 comments
- Oct 6, 2026chore(deps): bump the ntex group across 1 directory with 2 updates — graphql-hive/router0 reactions · 1 comment
- Sep 29, 2026chore(deps): bump ntex-util from 4.0.1 to 4.1.0 in the ntex group across 1 directory — graphql-hive/router0 reactions · 1 comment
- Sep 28, 2026chore(deps): bump the ntex group with 5 updates — graphql-hive/router0 reactions · 1 comment
- Sep 23, 2026chore(deps): bump the ntex group across 1 directory with 7 updates — graphql-hive/router0 reactions · 1 comment
- Sep 21, 2026chore(deps): bump the ntex group across 1 directory with 8 updates — graphql-hive/router0 reactions · 1 comment
- Sep 14, 2026chore(deps): bump the ntex group across 1 directory with 2 updates — graphql-hive/router0 reactions · 1 comment
- Sep 2, 2026chore(deps): update ntex requirement from =2.18.0 to =3.12.3 — easy-4-rust/thymeleaf-rust0 reactions · 2 comments
- Aug 30, 2026chore(deps): bump the ntex group across 1 directory with 2 updates — graphql-hive/router0 reactions · 1 comment
- Aug 27, 2026chore(deps): bump ntex-rt from 3.16.1 to 3.17.1 in the ntex group across 1 directory — graphql-hive/router0 reactions · 1 comment
- Aug 16, 2026chore(deps): bump the ntex group across 1 directory with 6 updates — graphql-hive/router0 reactions · 1 comment
- Aug 15, 2026build(deps): bump ntex from 3.10.1 to 3.12.3 — next-hat/metrs0 reactions · 1 comment
- Aug 10, 2026build(deps): bump ntex from 3.11.0 to 3.12.3 — next-hat/nanocl0 reactions · 2 comments
- Aug 8, 2026build(deps): bump ntex from 3.10.1 to 3.12.1 — next-hat/metrs0 reactions · 1 comment
- Aug 6, 2026chore(deps): bump the ntex group across 1 directory with 5 updates — graphql-hive/router0 reactions · 1 comment
- Jul 27, 2026build(deps): bump ntex from 3.10.1 to 3.11.0 — next-hat/nanocl0 reactions
- Jul 25, 2026build(deps): bump ntex from 3.10.1 to 3.11.0 — next-hat/metrs0 reactions · 1 comment
- Jul 20, 2026chore(deps): bump the ntex group across 1 directory with 4 updates — graphql-hive/router0 reactions · 5 comments
- May 19, 2026build(deps): bump ntex from 3.7.0 to 3.9.6 — vidhanio/hypertext0 reactions · 2 comments
- Dec 1, 2025`if_true` and `if_some` removed from `ResponseBuilder` — ntex-rs/ntex-extras0 reactions · 1 comment
- Dec 9, 2024build(deps): bump ntex from 2.7.0 to 2.10.0 — akupiec/ntex-starter-pack0 reactions
- Sep 25, 2024[Bug] Unable to build with different compilers and platforms after bumping `ntex` version — eclipse-zenoh/zenoh-plugin-mqtt0 reactions · 1 comment
- Jul 15, 2024Spawn async jobs in ntex — zino-rs/zino0 reactions · 1 comment
- Jun 24, 2024[RUST] Add Ntex — the-benchmarker/web-frameworks0 reactions
- Jun 16, 2023Add framework Ntex for Rust — the-benchmarker/website0 reactions · 1 comment
- Jan 27, 2023"undefined reference to" ld returned 1 exit status — rust-lang/rust0 reactions · 3 comments
On Hacker News · 1
- Jun 12, 2022Ntex – Framework for composable networking services3 points
On Bluesky · 1
- Mar 29, 2024Rust向けのネットワークサービス用フレームワーク。golangのechoのような機能は大体揃ってそう。HTTPだけでなくgRPCやMQTTなどの実装もある模様。 — @matsuu.bsky.social3 likes and reposts
In this repositoryOne other file it gives its agents.
- .github/copilot-instructions.mdCopilot instructions
The file
We split this llms.txt into the rules it gives an agent
7 rules. About 0 are common, 0 are distinctive, and 7 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈0
Distinctive · estimate
This author's own choices. Worth a look.
7
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 13
Use a Middleware struct plus a service with forward!
- Very specific: line 14
Write ctx.bind(&self.child) without a turbofish.
- Very specific: line 31
Read peer certificates with PeerCertDer / PeerCertChainDer, then parse with x509::PeerCerts.
- Very specific: line 36
Test the application the server runs.
- Very specific: line 37
Prefer in-process tests: web::test::init_service(App::new()...) with TestRequest and call_service opens no socket.
- Very specific: line 38
Use a real server (web::test::server, http::test::server, server::test_server) only for WebSocket, streaming, client, TLS, or connection …
- Very specific: line 39
Test a Service directly with Pipeline::new(state, service).call(req).await.
≈ “Common” is an estimate: at least 1 in 5 of the 3,592 llms.txt files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
# ntex
> ntex is a Rust framework for composable network services: HTTP/1, HTTP/2, WebSocket, web applications, and custom protocols. Everything is a `Service<St, Req>` with a pipeline state type `St`, executed by a `Pipeline` that coordinates readiness, calls, and shutdown.
Rules for generated code:
- A service implements `call(&self, req, ctx: Ctx<'_, Self, St>)`. Call child services with `ctx.call(&self.child, req)`, never `self.child.call(...)` directly.
- A service that wraps child services must forward `bind`, `ready`, and `shutdown` to every child. Use `ntex::forward!(St, field1, field2)`; add `map_err = f` when `Self::Error` differs from the children's error. `St` is the state type parameter of the impl.
- For web middleware that only runs code before or after the inner service, use `ntex::web::middleware::from_fn(async |req, next| ...)` and call `next.call(req).await?`. No annotations are needed, except `next: Next<'_, _, MyState>` when the closure reads `next.st()`. Return `Err(web::WebError::from_err(err))` to reject a request.
- For non-web services use `ntex::service::fn_layer(async |req: Req, ctx| ...)` with explicit request, response, and error types.
- Type-inference rules for service closures: always name the error type in `fn_service`, `fn_factory`, and `fn_layer` results (`Ok::<_, MyError>(..)`); annotate the argument of closures passed to `and_then`/`then` (`.and_then(async |res: Res| ...)`), or wrap them in `fn_service`. Closures for `map`, `map_err`, `map_init_err`, and `apply_fn` need no annotations.
- Type-inference rules for web code: inside `HttpServer::new` or `test::init_service` write `App::new()`; elsewhere write `App::default()` (state `()`) or `App::<AppState<T>>::new()`. Prefer `async fn` handlers; closure handlers must annotate every argument (`async |path: web::types::Path<String>| ...`) and name the error type of `Ok(..)` results, also in `filter` closures. `?` does not convert into `WebError`; return a concrete error type that implements `WebResponseError`, or use `.map_err(web::WebError::from_err)?`. Modular config functions take `cfg: &mut web::ServiceConfig` (state `()`) or `&mut web::ServiceConfig<St>`.
- Use a `Middleware` struct plus a service with `forward!` when the middleware needs its own configuration, readiness, or a named service type.
- Write `ctx.bind(&self.child)` without a turbofish.
- Implement `bind`, `ready`, and `shutdown` by hand only when the service has extra readiness sources, such as a shared `Pipeline` (`pipeline.bind_to(&ctx)`) or a `ReadyNotifier` (`ctx.subscribe(¬ifier)`).
- Async entry points use `#[ntex::main]`; async tests use `#[ntex::test]`.
## Server and client rules
- For HTTP applications, start with `web::HttpServer` and `web::App`. For custom TCP protocols or lower-level HTTP services, use `ntex::server::build()`; the standalone crate exposes the same builder as `ntex_server::net::build()`.
- Build servers inside an active ntex runtime. `server::build().bind(name, addr, SharedCfg::default(), async |state| service)` takes four arguments; `web::HttpServer::new(async |state| app).bind(addr, SharedCfg::default())` takes two. Factories are asynchronous even when construction does no asynchronous work. Call `.run().await` to wait for shutdown, not to check worker health.
- Factories run per worker and may run again after a restart. Construct `Rc`/`Cell`/`RefCell` state inside `build_with_config(async || Ok(state))`, not outside a factory that must be `Send`. Use `Arc` plus appropriate synchronization when data must be shared across workers. `SharedCfg` configures I/O and protocols; it is not application state.
- `HttpService::new(async |req| Ok(..))` needs an explicit error type (`Ok::<_, std::io::Error>(..)`). A named `async fn` with a concrete return type avoids this annotation. Finish response builders with `.build()`, not `.finish()`.
- For simple sequential protocols use `Io::recv(&codec)` and `Io::send(item, &codec)`. `BytesCodec` yields available byte chunks, not application-message boundaries. Use a framing codec for message protocols.
- `Dispatcher::new(io, codec, pipeline)` requires a `Pipeline` whose service accepts `DispatchItem<Codec>` and returns `Result<Option<EncodedItem>, E>`. Match `Item` separately from `Control`/`Stop` notifications; `Ok(None)` emits no response.
- Service factories for `server::test_server`, `http::test::server`, and `web::test::server` take `async |_| service_or_app`, with `&()` as the argument. `server::TestServerBuilder::with(cfg, factory)` passes `&Cfg::State` to the service factory. State-initialization closures remain zero-argument: `async || Ok(state)`. Use the test server's address, not a fixed port.
- Client send/body errors do not automatically convert into `std::io::Error`. Use a concrete application error or `Result<_, Box<dyn std::error::Error>>` to preserve them with `?`. If only a message is needed, explicitly use `.map_err(|err| std::io::Error::other(err.to_string()))?`; this loses the original error and diagnostic context. Do not require `Send + Sync` on the boxed error: client errors can be worker-local.
- TLS belongs in connection setup, not protocol logic. Compose acceptors/connectors with services over `Io`; use generic filters, `seal()`, or `boxed()` to keep protocol code independent of TLS backends.
- HTTP clients use `openssl`, `rustls`, or `secure_connector` for HTTPS; `ClientBuilder::connector` configures unsecured connections. Supply verifying TLS configurations explicitly: the built-in OpenSSL HTTP-client default currently disables certificate verification.
- `WsClient` needs an explicit TLS connector even for `wss://`: use `openssl`, `rustls`, or `connector`, and offer HTTP/1.1 rather than h2. Server-side WebSocket handlers work unchanged behind an HTTPS listener.
- Read peer certificates with `PeerCertDer` / `PeerCertChainDer`, then parse with `x509::PeerCerts`. Certificate presence or parsing is not trust verification or authorization. Schannel's `request_client_cert(true)` neither requires a certificate nor validates its trust; reject missing/untrusted identities before protected handling.
- See the [TLS guide](https://docs.ntex.rs/10-tls.html) and [runnable TLS examples](https://github.com/ntex-rs/examples/tree/main/tls) for backend, component, identity, and mutual-authentication configuration.
## Testing rules
- Test the application the server runs. Register routes in `fn config(cfg: &mut web::ServiceConfig)` (or `&mut web::ServiceConfig<St>`), call `App::new().configure(config)` in both `main` and the tests, and keep tests in `#[cfg(test)] mod tests` at the bottom of the file.
- Prefer in-process tests: `web::test::init_service(App::new()...)` with `TestRequest` and `call_service` opens no socket. Use `init_service_st(state.clone(), App::<St>::new()...)` for stateful apps and keep a clone of shared handles to inspect state.
- Use a real server (`web::test::server`, `http::test::server`, `server::test_server`) only for WebSocket, streaming, client, TLS, or connection behavior; it binds an available port. Never use fixed ports, external services, or sleeps for synchronization.
- Test a `Service` directly with `Pipeline::new(state, service).call(req).await`. Test codecs and `Io` protocols in memory with `ntex::io::testing::IoTest`.
- Assert the status and the body; for failures, assert the status code a client sees. Use `read_body` for bytes and `read_response_json` for JSON.
## Templates
Function service executed by a pipeline:
```rust
use ntex::service::{Pipeline, fn_service};
let service = fn_service(async |req: u32| Ok::<_, std::io::Error>(req + 1));
let pipeline = Pipeline::new((), service);
assert_eq!(pipeline.call(1).await?, 2);
```
Minimal web server:
```rust
use ntex::{SharedCfg, web};
#[web::get("/")]
async fn index() -> &'static str {
"Hello world!"
}
#[ntex::main]
async fn main() -> std::io::Result<()> {
web::HttpServer::new(async |_| web::App::new().service(index))
.bind("127.0.0.1:8080", SharedCfg::new("hello-world"))?
.run()
.await
}
```
Closure web middleware (preferred):
```rust
use ntex::http::header::{HeaderName, HeaderValue};
use ntex::web::{self, App, HttpResponse, middleware::from_fn};
web::HttpServer::new(async |_| {
App::new()
.middleware(from_fn(async |req, next| {
if !req.headers().contains_key("authorization") {
return Ok(req.into_response(HttpResponse::Unauthorized().build()));
}
let mut res = next.call(req).await?;
res.headers_mut().insert(
HeaderName::from_static("x-application"),
HeaderValue::from_static("example"),
);
Ok(res)
}))
.route("/", web::get().to(async || "Hello"))
});
```
Struct middleware with forwarding:
```rust
use std::time::Instant;
use ntex::web::{WebRequest, WebResponse};
use ntex::{Ctx, Middleware, Service};
pub struct Timing;
pub struct TimingService<S> {
service: S,
}
impl<S, St> Middleware<S, St> for Timing {
type Service = TimingService<S>;
fn create(&self, _: &St, service: S) -> Self::Service {
TimingService { service }
}
}
impl<S, St, ReqSt> Service<St, WebRequest<ReqSt>> for TimingService<S>
where
S: Service<St, WebRequest<ReqSt>, Res = WebResponse>,
{
type Res = WebResponse;
type Error = S::Error;
ntex::forward!(St, service);
async fn call(
&self,
req: WebRequest<ReqSt>,
ctx: Ctx<'_, Self, St>,
) -> Result<WebResponse, S::Error> {
let start = Instant::now();
let res = ctx.call(&self.service, req).await;
log::info!("request took {:?}", start.elapsed());
res
}
}
```
Several children with error conversion:
```rust
use ntex::{Ctx, Service};
pub enum AuthError<E> {
Denied,
Service(E),
}
pub struct Authorized<A, S> {
check: A,
service: S,
}
impl<A, S, St, Req> Service<St, Req> for Authorized<A, S>
where
A: Service<St, Req, Res = (Req, bool), Error = S::Error>,
S: Service<St, Req>,
{
type Res = S::Res;
type Error = AuthError<S::Error>;
ntex::forward!(St, check, service, map_err = AuthError::Service);
async fn call(&self, req: Req, ctx: Ctx<'_, Self, St>) -> Result<S::Res, Self::Error> {
let (req, allowed) = ctx.call(&self.check, req).await.map_err(AuthError::Service)?;
if !allowed {
return Err(AuthError::Denied);
}
ctx.call(&self.service, req).await.map_err(AuthError::Service)
}
}
```
Explicit forwarding with a shared pipeline child:
```rust
use ntex::service::{BindCtx, Ctx, Pipeline, Service};
pub struct Cached<S, E> {
service: S,
// shared with other services, owned and shut down elsewhere
cache: Pipeline<String, Option<String>, E>,
}
impl<S, St, E> Service<St, String> for Cached<S, E>
where
S: Service<St, String, Res = String, Error = E>,
E: 'static,
{
type Res = String;
type Error = E;
fn bind(&self, ctx: BindCtx<'_>) {
ctx.bind(&self.service);
self.cache.bind_to(&ctx);
}
async fn ready(&self, ctx: Ctx<'_, Self, St>) -> Result<(), E> {
self.cache.ready().await?;
ctx.ready(&self.service).await
}
async fn shutdown(&self, ctx: Ctx<'_, Self, St>) {
// the shared cache pipeline is shut down by its owner
ctx.shutdown(&self.service).await;
}
async fn call(&self, req: String, ctx: Ctx<'_, Self, St>) -> Result<String, E> {
if let Some(res) = self.cache.call(req.clone()).await? {
return Ok(res);
}
ctx.call(&self.service, req).await
}
}
```
TCP byte-echo server:
```rust
use ntex::{SharedCfg, codec::BytesCodec, io::Io, service::fn_service};
async fn echo(io: Io) -> std::io::Result<()> {
while let Some(msg) = io.recv(&BytesCodec).await? {
io.send(msg, &BytesCodec).await?;
}
Ok(())
}
#[ntex::main]
async fn main() -> std::io::Result<()> {
ntex::server::build()
.bind("echo", "127.0.0.1:8080", SharedCfg::default(), async |_| fn_service(echo))?
.run()
.await
}
```
Dispatcher-based byte-echo server (control and stop notifications need no response here):
```rust
use ntex::codec::BytesCodec;
use ntex::io::{DispatchItem, Dispatcher, Io};
use ntex::service::{Pipeline, fn_service};
async fn serve(io: Io) -> std::io::Result<()> {
let handler = fn_service(async |item: DispatchItem<BytesCodec>| match item {
DispatchItem::Item(frame) => Ok::<_, std::io::Error>(Some(frame)),
_ => Ok(None),
});
Dispatcher::new(io, BytesCodec, Pipeline::new((), handler)).await
}
#[ntex::main]
async fn main() -> std::io::Result<()> {
ntex::server::build()
.bind("echo", "127.0.0.1:8080", ntex::SharedCfg::default(), async |_| fn_service(serve))?
.run()
.await
}
```
Low-level HTTP server without `web::App`:
```rust
use ntex::SharedCfg;
use ntex::http::{HttpService, Request, Response};
async fn handle(req: Request) -> Result<Response, std::io::Error> {
Ok(Response::Ok().body(format!("path: {}", req.path())))
}
#[ntex::main]
async fn main() -> std::io::Result<()> {
ntex::server::build()
.bind("http", "127.0.0.1:8080", SharedCfg::default(), async |_| HttpService::new(handle))?
.run()
.await
}
```
Network test with `test_server` and `Client`:
```rust
use ntex::client::Client;
use ntex::http::{HttpService, Response};
#[ntex::test]
async fn http_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
let srv = ntex::server::test_server(async |_| {
HttpService::new(async |_| Ok::<_, std::io::Error>(Response::Ok().body("hello")))
});
let url = format!("http://{}/", srv.addr());
let res = Client::new().get(url.as_str()).send().await?;
assert!(res.status().is_success());
assert_eq!(res.body().await?, "hello");
Ok(())
}
```
In-process route test that uses the application's config:
```rust
use ntex::http::StatusCode;
use ntex::web::{self, App, HttpResponse, test};
use serde::{Deserialize, Serialize};
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct User {
name: String,
}
async fn create(user: web::types::Json<User>) -> HttpResponse {
HttpResponse::Created().json(&user.into_inner())
}
pub fn config(cfg: &mut web::ServiceConfig) {
cfg.route("/users", web::post().to(create));
}
#[cfg(test)]
mod tests {
use super::*;
#[ntex::test]
async fn create_user() {
let app = test::init_service(App::new().configure(config)).await;
let user = User { name: "alice".into() };
let req = test::TestRequest::post().uri("/users").json(&user).to_request();
let res = test::call_service(&app, req).await;
assert_eq!(res.status(), StatusCode::CREATED);
let body: User = serde_json::from_slice(&test::read_body(res).await).unwrap();
assert_eq!(body, user);
let req = test::TestRequest::post().uri("/users").header("content-type", "application/json").payload("{").to_request();
let res = test::call_service(&app, req).await;
assert_eq!(res.status(), StatusCode::BAD_REQUEST);
}
}
```
Stateful route test (keep a clone of the shared handle to inspect state):
```rust
use std::{cell::Cell, rc::Rc};
use ntex::web::{self, App, AppState, test};
type Hits = AppState<Rc<Cell<usize>>>;
async fn hit(hits: &Hits, (): ()) -> String {
hits.set(hits.get() + 1);
hits.get().to_string()
}
pub fn config(cfg: &mut web::ServiceConfig<Hits>) {
cfg.route("/hit", web::post().to_with_state(hit));
}
#[ntex::test]
async fn counts_hits() {
let hits = Hits::default();
let app = test::init_service_st(hits.clone(), App::<Hits>::new().configure(config)).await;
for expected in ["1", "2"] {
let req = test::TestRequest::post().uri("/hit").to_request();
let res = test::call_service(&app, req).await;
assert!(res.status().is_success());
assert_eq!(test::read_body(res).await, expected);
}
assert_eq!(hits.get(), 2);
}
```
WebSocket test against a real test server:
```rust
use ntex::util::Bytes;
use ntex::web::{self, App, HttpRequest, test, ws};
async fn echo(frame: ws::Frame) -> Result<Option<ws::Message>, std::io::Error> {
Ok(match frame {
ws::Frame::Text(text) => Some(ws::Message::Text(String::from_utf8_lossy(&text).as_ref().into())),
ws::Frame::Ping(msg) => Some(ws::Message::Pong(msg)),
ws::Frame::Close(reason) => Some(ws::Message::Close(reason)),
_ => None,
})
}
async fn socket(req: HttpRequest) {
let _ = ws::start(&req, None, echo).await;
}
#[ntex::test]
async fn ws_echo() -> Result<(), Box<dyn std::error::Error>> {
let srv = test::server(async |_| App::new().route("/ws", web::get().to(socket)));
let (io, codec, _) = srv.ws_at("/ws").await?.into_inner();
io.send(ws::Message::Text("hello".into()), &codec).await?;
assert_eq!(io.recv(&codec).await?, Some(ws::Frame::Text(Bytes::from_static(b"hello"))));
Ok(())
}
```
Protocol test without sockets, for `echo` from the TCP byte-echo template:
```rust
use ntex::io::testing::IoTest;
#[ntex::test]
async fn echo_in_memory() {
let (client, server) = IoTest::create();
client.remote_buffer_cap(1024);
let _task = ntex::rt::spawn(echo(Io::from(server)));
client.write(b"ping");
assert_eq!(client.read().await.unwrap(), b"ping"[..]);
}
```
Worker-local state (counts accepted connections, then closes them):
```rust
use std::{cell::Cell, rc::Rc};
use ntex::{SharedCfg, io::Io};
#[derive(Clone, Default)]
struct Connections(Rc<Cell<usize>>);
async fn handle(st: &Connections, _io: Io) -> std::io::Result<()> {
st.0.set(st.0.get() + 1);
Ok(())
}
#[ntex::main]
async fn main() -> std::io::Result<()> {
ntex::server::build_with_config(async || Ok(Connections::default()))
.bind("svc", "127.0.0.1:8080", SharedCfg::default(), async |_| ntex::service(handle))?
.run()
.await
}
```
## Coming from actix-web
| actix-web | ntex |
|---|---|
| `#[actix_web::main]`, `#[actix_web::test]` | `#[ntex::main]`, `#[ntex::test]` |
| `HttpServer::new(\|\| App::new())` | `web::HttpServer::new(async \|_\| App::new())` or `web::server(..)` |
| `.bind(("127.0.0.1", 8080))?` | `.bind("127.0.0.1:8080", SharedCfg::default())?` |
| `.wrap(m)` | `.middleware(m)` on `App`, `Scope`, `Resource` |
| `middleware::from_fn`, `wrap_fn` | `web::middleware::from_fn(async \|req, next\| next.call(req).await)` |
| `middleware::{Logger, DefaultHeaders, Compress}` | same names in `web::middleware` (`Compress` needs feature `compress`) |
| `web::Data<T>` + `.app_data(..)` | `web::AppState<T>` from `web::server_with_config(async \|\| Ok(AppState::new(v)), ..)`, read by handlers registered with `.to_with_state(h)` where `async fn h(st: &AppState<T>, (): (), ..)` |
| `web::Json/Path/Query/Form/Payload` | `web::types::Json/Path/Query/Form/Payload` |
| `Bytes`, `Either` | `ntex::util::Bytes`, `ntex::util::Either` |
| `ResponseError` | `web::WebResponseError<St, web::DefaultError>` with `fn error_response(&self, st: &St) -> HttpResponse`; type must impl `std::error::Error` |
| `actix_web::Error` | `web::WebError`; `?` does not convert, use `.map_err(web::WebError::from_err)?` |
| `HttpResponse::Ok().finish()` | `HttpResponse::Ok().build()`; `.json(&value)` takes a reference |
| `ServiceRequest`, `ServiceResponse` | `web::WebRequest`, `web::WebResponse` |
| `#[get("/")]` | `#[web::get("/")]` |
| `actix_web::rt::spawn` | `ntex::rt::spawn` |
| `actix_web::test::*` | `ntex::web::test::{init_service, call_service, read_body, TestRequest}` |
`scope`, `resource`, `route`, `service`, `to`, `configure`, `ServiceConfig` and `default_service` keep their actix-web names.
## Guide
- [Service Model](https://docs.ntex.rs/1-service.html): the `Service` trait, factories, and function services
- [Service Pipelines](https://docs.ntex.rs/2-pipeline.html): readiness, binding, forwarding, and shutdown
- [Service State](https://docs.ntex.rs/3-state.html)
- [Runtime](https://docs.ntex.rs/4-runtime.html)
- [Server](https://docs.ntex.rs/5-server.html)
- [Worker and Request State](https://docs.ntex.rs/6-server-app.html)
- [I/O Abstraction Layer](https://docs.ntex.rs/7-io.html)
- [Applications and Routing](https://docs.ntex.rs/8.1-web-app.html)
- [Application State](https://docs.ntex.rs/8.2-web-app.html)
- [Filters and Middleware](https://docs.ntex.rs/8.3-web-app.html)
- [Extractors and Responders](https://docs.ntex.rs/8.4-web-app.html)
- [Error Handling](https://docs.ntex.rs/8.5-web-app.html)
- [WebSocket Support](https://docs.ntex.rs/8.6-web-app.html)
- [Byte Buffers](https://docs.ntex.rs/9-bytes.html)
- [Securing Connections with TLS](https://docs.ntex.rs/10-tls.html): backend and component configuration, identities, peer certificates, and mutual authentication
- [Making HTTP Requests](https://docs.ntex.rs/13-client.html): the `ntex::client` HTTP/1.1 and HTTP/2 client, connection pooling, `ClientConfig` and timeouts, TLS connectors, request bodies, reading responses, and client middleware
- [HTTP/2 with ntex-h2](https://docs.ntex.rs/14-h2.html): standalone HTTP/2 client and server, `ServiceConfig`, pooled and single-connection clients, streams and resets, `Capacity` and flow control, publish and control services
- [MQTT with ntex-mqtt](https://docs.ntex.rs/15-mqtt.html): MQTT 3.1.1 and 5 clients and servers, `MqttServiceConfig`, publishing and acks, streaming payloads, flow control, subscriptions, routing, `MqttSink`, protocol and control services
- [Timers and Time Utilities](https://docs.ntex.rs/12-time.html): timer wheel design and precision, `Millis` and `Seconds`, `sleep`, `deadline`, `timeout`, `interval`, and callback timers
## API
- [ntex](https://docs.ntex.rs/api/ntex/index.html)
- [ntex_service](https://docs.ntex.rs/api/ntex_service/index.html): `Service`, `Middleware`, `Pipeline`, `forward!`, `fn_layer`
- [ntex::web](https://docs.ntex.rs/api/ntex/web/index.html): `App`, `HttpServer`, `middleware::from_fn`
- [ntex_server::net](https://docs.ntex.rs/api/ntex_server/net/index.html): `build`, `build_with_config`, `ServerBuilder`, `test_server`
- [ntex::tls](https://docs.ntex.rs/api/ntex/tls/index.html): acceptors, connectors, `TlsConfig`, and portable `x509` identities and trust
## Optional
- [Full guide](https://docs.ntex.rs/llms-full.txt): every guide chapter in one file
- [Examples](https://github.com/ntex-rs/examples)
- [Source](https://github.com/ntex-rs/ntex)
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

