contentstack-api-client-rs 0.1.0

Async Rust client for the Contentstack CMS API, supporting both Delivery (CDN) and Management APIs with built-in rate limiting and streaming.
Documentation

contentstack-api-client-rs

CI Crates.io Docs.rs License

Async Rust HTTP client for the Contentstack CMS API.

  • Delivery API - fetch entries from the CDN (available)
  • Management API - CRUD on entries, assets, content types (planned)

Installation

[dependencies]
contentstack-api-client-rs = "0.1"

Quick Start

use serde::Deserialize;
use contentstack_api_client_rs::{Delivery, GetManyParams, GetOneParams, Query};
use serde_json::json;

#[derive(Deserialize)]
struct BlogPost {
    pub body: String,
    pub url: String,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = Delivery::new("YOUR_API_KEY", "YOUR_DELIVERY_TOKEN", "production", None);

    // Fetch multiple entries
    let response = client
        .entries()
        .get_many::<BlogPost>("blog_post", None)
        .await?;

    println!("Found {} entries", response.entries.len());

    // Fetch with filters and pagination
    let mut query = Query::new();
    query.insert("title".into(), json!("Hello World"));

    let filtered = client
        .entries()
        .get_many::<BlogPost>("blog_post", Some(GetManyParams {
            query: Some(&query),
            limit: Some(10),
            skip: Some(0),
            include_count: Some(true),
            locale: Some("en-us"),
        }))
        .await?;

    println!("Total: {:?}", filtered.count);

    // Fetch a single entry by UID
    let entry = client
        .entries()
        .get_one::<BlogPost>("blog_post", "entry_uid_123", None)
        .await?;

    println!("Title: {}", entry.entry.title);

    Ok(())
}

Configuration

use contentstack_api_client_rs::{Delivery, ClientOptions, Region};
use std::time::Duration;

let client = Delivery::new(
    "YOUR_API_KEY",
    "YOUR_DELIVERY_TOKEN",
    "production",
    Some(ClientOptions {
        region: Some(Region::AwsEu),       // default: AwsNa
        timeout: Some(Duration::from_secs(10)), // default: 30s
        max_connections: Some(20),          // default: 50
        base_url: None,                     // override CDN URL if needed
    }),
);

Supported regions

Region Delivery URL
AwsNa (default) https://cdn.contentstack.io
AwsEu https://eu-cdn.contentstack.com
AwsAu https://au-cdn.contentstack.com
AzureNa https://azure-na-cdn.contentstack.com
AzureEu https://azure-eu-cdn.contentstack.com
GcpNa https://gcp-na-cdn.contentstack.com
GcpEu https://gcp-eu-cdn.contentstack.com

Custom Entry Fields

Define a struct for your content type's custom fields - system fields (uid, title, locale, etc.) are always included automatically:

use serde::Deserialize;
use contentstack_api_client_rs::Entry;

#[derive(Deserialize)]
struct BlogPost {
    pub body: String,
    pub url: String,
    pub author: String,
}

// entry: Entry<BlogPost>
// entry.uid        - system field
// entry.title      - system field
// entry.fields.body - your custom field

MSRV

Rust 1.93.1 or later. Edition 2024.

Contributing

Commits must follow Conventional Commits - enforced by CI:

Prefix Effect
feat: minor version bump
fix: patch version bump
feat!: / BREAKING CHANGE: major version bump
chore:, docs:, ci: no version bump
cargo fmt
cargo clippy --all-targets --all-features -- -D warnings
cargo test --all-features

Making a Release

Releases are fully automated via release-plz:

  1. Merge one or more PRs to main with Conventional Commit messages
  2. release-plz automatically opens a release PR with an updated Cargo.toml version and CHANGELOG.md
  3. Review and merge the release PR
  4. release-plz tags the commit, publishes to crates.io, and creates a GitHub release

No manual tagging or version bumping required.

License

Licensed under either of MIT or Apache-2.0 at your option.