Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Application Patterns

This page maps common application design patterns to the ndn-rs APIs that implement them. Each pattern includes the recommended crate/type, and a short code snippet showing the key call.


Fetch Content Once

Fetch a named piece of content by name and wait for a response.

API: ndn_app::Consumer::get or Consumer::fetch

#![allow(unused)]
fn main() {
let mut consumer = Consumer::connect("/run/nfd/nfd.sock").await?;
let bytes = consumer.get("/example/data").await?;
}

To set a hop limit, forwarding hint, or application parameters, use Consumer::fetch_with with an InterestBuilder:

#![allow(unused)]
fn main() {
use ndn_packet::encode::InterestBuilder;

// Limit to 4 forwarding hops
let data = consumer.fetch_with(
    InterestBuilder::new("/ndn/remote/data").hop_limit(4)
).await?;

// Reach a producer via a delegation prefix (forwarding hint)
let data = consumer.fetch_with(
    InterestBuilder::new("/alice/files/photo.jpg")
        .forwarding_hint(vec!["/campus/ndn-hub".parse()?])
).await?;

// Parameterised fetch — ApplicationParameters triggers
// ParametersSha256DigestComponent auto-appended to the name
let data = consumer.fetch_with(
    InterestBuilder::new("/service/query")
        .app_parameters(b"filter=recent&limit=10")
).await?;
}

The local receive timeout is derived automatically from the Interest lifetime (+ 500 ms buffer). Call fetch_wire directly if you need a non-standard timeout.


Serve Content on Demand

Register a prefix and respond to Interests with dynamically generated Data.

API: ndn_app::Producer::connect + serve

#![allow(unused)]
fn main() {
let mut producer = Producer::connect("/run/nfd/nfd.sock", "/sensor").await?;
producer.serve(|interest| async move {
    Some(DataBuilder::new((*interest.name).clone(), b"42").build())
}).await
}

Subscribe to a Live Data Stream

Receive all new data published to a shared group prefix (SVS-based sync).

API: ndn_app::Subscriber

#![allow(unused)]
fn main() {
let mut sub = Subscriber::connect(
    "/run/nfd/nfd.sock",
    "/chat/room1",
    SubscriberConfig::default(),
).await?;
while let Some(sample) = sub.recv().await {
    println!(
        "[{}] {}",
        sample.publisher,
        String::from_utf8_lossy(&sample.payload.unwrap_or_default()),
    );
}
}

Request/Response (RPC-style)

Handle request-response pairs where each reply goes only to the querying consumer.

API: ndn_app::Queryable

#![allow(unused)]
fn main() {
let mut queryable = Queryable::connect("/run/nfd/nfd.sock", "/compute").await?;
while let Some(query) = queryable.recv().await {
    let result = do_work(query.interest());
    query.reply(
        DataBuilder::new(query.interest().name().clone())
            .content(result.as_bytes())
            .build_unsigned(),
    ).await?;
}
}

Transfer Large Content (Segmented)

Transfer content larger than a single packet, with automatic segmentation and reassembly.

API: ndn_app::ChunkedProducer + ChunkedConsumer

#![allow(unused)]
fn main() {
// Producer side
ChunkedProducer::connect(socket, "/files/report.pdf", &file_bytes).await?;

// Consumer side
let bytes = ChunkedConsumer::connect(socket).fetch("/files/report.pdf").await?;
}

Verify Content Before Use

Fetch Data and cryptographically verify it against a trust schema before use. The SafeData type ensures only verified data reaches sensitive code paths.

API: Consumer::fetch_verified + KeyChain

#![allow(unused)]
fn main() {
let keychain = KeyChain::load_or_init("/etc/ndn/keys").await?;
let safe_data = consumer
    .fetch_verified("/example/data", &keychain.validator().await?)
    .await?;
// safe_data: SafeData — compiler-enforced proof of verification
}

Embedded / Mobile (No External Router)

Run the full NDN forwarding engine inside your binary. No system daemon required.

For Android / iOS: use ndn_mobile::MobileEngine — a pre-configured wrapper with mobile-tuned defaults, lifecycle suspend/resume, and Bluetooth face support. See the Mobile Apps guide.

#![allow(unused)]
fn main() {
// ndn-mobile: one-liner setup, mobile-tuned defaults
use ndn_mobile::{Consumer, MobileEngine};

let (engine, handle) = MobileEngine::builder().build().await?;
let mut consumer = Consumer::from_handle(handle);
let mut producer = engine.register_producer("/my/prefix");
}

For desktop / testing: use ndn_app::EngineBuilder directly. See the Embedded Engine section.

#![allow(unused)]
fn main() {
use ndn_app::EngineBuilder;
use ndn_engine::EngineConfig;
use ndn_faces::local::InProcFace;
use ndn_transport::FaceId;

let mut builder = EngineBuilder::new(EngineConfig::default());
let app_face_id = builder.alloc_face_id();
let (face, handle) = InProcFace::new(app_face_id, 64);
let (engine, _shutdown) = builder.face(face).build().await?;
let mut consumer = ndn_app::Consumer::from_handle(handle);
}

Publish State to a Sync Group

Publish local state updates to a distributed sync group; all members receive updates.

API: ndn_sync::join_svs_group + SyncHandle

#![allow(unused)]
fn main() {
let sync = join_svs_group(&engine, "/chat/room1", "/ndn/mynode").await?;
sync.publish(b"hello everyone".to_vec()).await?;
while let Some(update) = sync.recv().await {
    println!("from {}: {:?}", update.name, update.data);
}
}

Custom Forwarding Strategy

Override the default forwarding decision for a name prefix.

API: ndn_strategy::Strategy trait + EngineBuilder::strategy

#![allow(unused)]
fn main() {
struct MyStrategy;
impl Strategy for MyStrategy {
    fn on_interest(&self, ctx: &StrategyContext, interest: &Interest) -> ForwardingAction {
        // Custom forwarding logic
        ForwardingAction::Forward(ctx.fib_lookup(interest.name()))
    }
    // on_nack and on_data_in omitted for brevity
}
let engine = EngineBuilder::new(config)
    .strategy("/my/prefix", MyStrategy)
    .build()
    .await?;
}

Peer Discovery and Auto-FIB

Discover NDN neighbors on the local network and automatically populate the FIB.

API: ndn_discovery::UdpNeighborDiscovery + EngineBuilder::discovery

#![allow(unused)]
fn main() {
let discovery = UdpNeighborDiscovery::new(config)?;
let engine = EngineBuilder::new(config)
    .discovery(discovery)
    .build()
    .await?;
// FIB entries for discovered neighbors are installed automatically
}

Integration Testing with Simulated Topology

Spin up a full forwarding engine in tests without any external processes or network.

API: ndn_sim::Simulation

#![allow(unused)]
fn main() {
let mut sim = Simulation::new();
let router = sim.add_router("r1");
let producer = sim.add_producer("p1", "/test");
sim.add_link(router, producer, LinkConfig::default());
let result = sim.send_interest(consumer, "/test/data").await?;
assert!(result.is_ok());
}

Synchronous / Non-Async Applications

Use NDN in blocking code (Python extensions, CLI tools, non-async Rust).

API: ndn_app::blocking::{BlockingConsumer, BlockingProducer}

#![allow(unused)]
fn main() {
let mut consumer = BlockingConsumer::connect("/run/nfd/nfd.sock")?;
let bytes = consumer.get("/example/hello")?;
}

Embedded / Constrained Devices (no_std)

Run a minimal forwarder on ARM Cortex-M or RISC-V with no heap allocator.

API: ndn_embedded::Forwarder (const-generic, no_std)

Refer to the Embedded Targets guide.


For a deeper walkthrough of the most common patterns, see Building NDN Applications.