| Crates.io | lmrc-cloudflare |
| lib.rs | lmrc-cloudflare |
| version | 0.3.16 |
| created_at | 2025-11-26 17:44:09.795641+00 |
| updated_at | 2025-12-11 13:26:59.475874+00 |
| description | Cloudflare API client library for the LMRC Stack - comprehensive DNS, zones, and cache management with automatic retry logic |
| homepage | https://gitlab.com/lemarco/lmrc-stack/tree/main/libs/cloudflare-client |
| repository | https://gitlab.com/lemarco/lmrc-stack |
| max_upload_size | |
| id | 1951915 |
| size | 186,229 |
Part of the LMRC Stack - Infrastructure-as-Code toolkit for building production-ready Rust applications
A comprehensive, well-documented Rust client for the Cloudflare API, designed specifically for CI/CD and automation workflows.
Add this to your Cargo.toml:
[dependencies]
lmrc-cloudflare = "0.2"
tokio = { version = "1", features = ["full"] }
use lmrc_cloudflare::{CloudflareClient, dns::RecordType};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create a client
let client = CloudflareClient::builder()
.api_token("your-api-token")
.build()?;
// Get zone ID
let zone_id = client.zones().get_zone_id("example.com").await?;
// Create a DNS record
let record = client.dns()
.create_record(&zone_id)
.name("api.example.com")
.record_type(RecordType::A)
.content("192.0.2.1")
.proxied(true)
.send()
.await?;
println!("Created record: {}", record.id);
Ok(())
}
// List all DNS records for a zone
let records = client.dns()
.list_records(&zone_id)
.send()
.await?;
// Filter by type
let a_records = client.dns()
.list_records(&zone_id)
.record_type(RecordType::A)
.send()
.await?;
// Filter by name and type
let specific_record = client.dns()
.list_records(&zone_id)
.name("api.example.com")
.record_type(RecordType::A)
.send()
.await?;
use lmrc_cloudflare::dns::RecordType;
// Create an A record
let record = client.dns()
.create_record(&zone_id)
.name("api.example.com")
.record_type(RecordType::A)
.content("192.0.2.1")
.proxied(true)
.ttl(1)
.comment("API server")
.send()
.await?;
// Create a CNAME record
let record = client.dns()
.create_record(&zone_id)
.name("www.example.com")
.record_type(RecordType::CNAME)
.content("example.com")
.proxied(true)
.send()
.await?;
// Update record content
let updated = client.dns()
.update_record(&zone_id, &record_id)
.content("192.0.2.2")
.send()
.await?;
// Update multiple fields
let updated = client.dns()
.update_record(&zone_id, &record_id)
.content("192.0.2.3")
.proxied(false)
.ttl(3600)
.send()
.await?;
client.dns()
.delete_record(&zone_id, &record_id)
.await?;
// Find a record by name and type
let record = client.dns()
.find_record(&zone_id, "api.example.com", RecordType::A)
.await?;
if let Some(record) = record {
println!("Found: {} -> {}", record.name, record.content);
}
Perfect for CI/CD pipelines where you want to ensure DNS records match a desired state:
use lmrc_cloudflare::dns::{RecordType, DnsRecordBuilder};
// Define desired state
let desired_records = vec![
DnsRecordBuilder::new()
.name("api.example.com")
.record_type(RecordType::A)
.content("192.0.2.1")
.proxied(true),
DnsRecordBuilder::new()
.name("www.example.com")
.record_type(RecordType::CNAME)
.content("example.com")
.proxied(true),
];
// Dry run - see what would change without applying
let changes = client.dns()
.sync_records(&zone_id)
.records(desired_records.clone())
.dry_run(true)
.send()
.await?;
for change in &changes {
match change.action {
ChangeAction::Create => println!("Would create: {}", change.description),
ChangeAction::Update => println!("Would update: {}", change.description),
ChangeAction::NoChange => println!("Already correct: {}", change.description),
_ => {}
}
}
// Apply changes
let changes = client.dns()
.sync_records(&zone_id)
.records(desired_records)
.dry_run(false)
.send()
.await?;
println!("Applied {} changes", changes.iter().filter(|c| c.action.is_mutating()).count());
// List all zones
let zones = client.zones()
.list()
.send()
.await?;
for zone in zones {
println!("{}: {}", zone.name, zone.id);
}
// Get a specific zone
let zone = client.zones()
.get(&zone_id)
.await?;
// Find zone by name
let zone = client.zones()
.find_by_name("example.com")
.await?;
// Get zone ID by name (convenience method)
let zone_id = client.zones()
.get_zone_id("example.com")
.await?;
// Purge everything (careful!)
client.cache()
.purge_everything(&zone_id)
.await?;
// Purge specific URLs
client.cache()
.purge_urls(&zone_id)
.urls(vec![
"https://example.com/page1",
"https://example.com/page2",
])
.send()
.await?;
// Purge by cache tags (Enterprise plan)
client.cache()
.purge_tags(&zone_id)
.tags(vec!["product", "blog"])
.send()
.await?;
// Purge by hosts (Enterprise plan)
client.cache()
.purge_hosts(&zone_id)
.hosts(vec!["www.example.com", "api.example.com"])
.send()
.await?;
// Purge by prefixes (Enterprise plan)
client.cache()
.purge_prefixes(&zone_id)
.prefixes(vec!["example.com/images/", "example.com/videos/"])
.send()
.await?;
The library provides detailed error types for better error handling:
use lmrc_cloudflare::Error;
match client.zones().get_zone_id("example.com").await {
Ok(zone_id) => println!("Zone ID: {}", zone_id),
Err(Error::NotFound(msg)) => eprintln!("Zone not found: {}", msg),
Err(Error::Unauthorized(msg)) => eprintln!("Authentication failed: {}", msg),
Err(Error::RateLimited { retry_after }) => {
eprintln!("Rate limited, retry after: {:?} seconds", retry_after);
}
Err(Error::Api(api_error)) => {
eprintln!("API error: {} (code: {:?})", api_error.message, api_error.code);
}
Err(e) => eprintln!("Error: {}", e),
}
name: Update DNS
on:
push:
branches: [main]
jobs:
update-dns:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Update DNS records
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
run: cargo run --example sync-dns
use lmrc_cloudflare::{CloudflareClient, dns::{RecordType, DnsRecordBuilder}};
use std::env;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let api_token = env::var("CLOUDFLARE_API_TOKEN")?;
let client = CloudflareClient::new(api_token)?;
let zone_id = client.zones().get_zone_id("example.com").await?;
let records = vec![
DnsRecordBuilder::new()
.name("api.example.com")
.record_type(RecordType::A)
.content(env::var("API_SERVER_IP")?)
.proxied(true),
];
let changes = client.dns()
.sync_records(&zone_id)
.records(records)
.dry_run(false)
.send()
.await?;
for change in changes {
println!("{:?}: {}", change.action, change.description);
}
Ok(())
}
The client supports Cloudflare API tokens. To create an API token:
let client = CloudflareClient::builder()
.api_token("your-api-token")
.build()?;
A - IPv4 addressAAAA - IPv6 addressCNAME - Canonical nameMX - Mail exchangeTXT - Text recordSRV - Service locatorNS - Name serverCAA - Certificate authority authorizationPTR - Pointer recordContributions are welcome! Please feel free to submit a Pull Request.
Part of the LMRC Stack project. Licensed under either of:
at your option.
This client is not officially associated with Cloudflare, Inc.