# Cloudflare Client - Public API Documentation
This document provides a comprehensive overview of the public API for the `cloudflare-client` crate.
## Table of Contents
1. [Client Creation](#client-creation)
2. [DNS Service](#dns-service)
3. [Zone Service](#zone-service)
4. [Cache Service](#cache-service)
5. [Error Handling](#error-handling)
6. [Types](#types)
---
## Client Creation
### `CloudflareClient`
The main client for interacting with the Cloudflare API.
```rust
use lmrc_cloudflare::CloudflareClient;
// Method 1: Using builder (recommended)
let client = CloudflareClient::builder()
.api_token("your-api-token")
.timeout(std::time::Duration::from_secs(60)) // optional
.build()?;
// Method 2: Direct creation
let client = CloudflareClient::new("your-api-token")?;
```
### `CloudflareClientBuilder`
Builder for creating `CloudflareClient` instances.
**Methods:**
- `api_token(token: impl Into<String>) -> Self` - Set the API token (required)
- `base_url(url: impl Into<String>) -> Self` - Set custom base URL (optional)
- `timeout(duration: Duration) -> Self` - Set request timeout (optional, default: 30s)
- `build() -> Result<CloudflareClient>` - Build the client
---
## DNS Service
Access via `client.dns()`.
### Operations
#### List DNS Records
```rust
let records = client.dns()
.list_records("zone_id")
.record_type(RecordType::A) // optional filter
.name("api.example.com") // optional filter
.content("192.0.2.1") // optional filter
.page(1) // optional pagination
.per_page(50) // optional pagination
.send()
.await?;
```
#### Get DNS Record
```rust
let record = client.dns()
.get_record("zone_id", "record_id")
.await?;
```
#### Create DNS Record
```rust
let record = client.dns()
.create_record("zone_id")
.name("api.example.com")
.record_type(RecordType::A)
.content("192.0.2.1")
.proxied(true) // optional
.ttl(1) // optional (1 = auto)
.comment("API server") // optional
.priority(10) // optional (MX, SRV)
.send()
.await?;
```
#### Update DNS Record
```rust
let record = client.dns()
.update_record("zone_id", "record_id")
.content("192.0.2.2")
.proxied(false) // optional
.ttl(3600) // optional
.send()
.await?;
```
#### Delete DNS Record
```rust
client.dns()
.delete_record("zone_id", "record_id")
.await?;
```
#### Find DNS Record
```rust
let record = client.dns()
.find_record("zone_id", "api.example.com", RecordType::A)
.await?; // Returns Option<DnsRecord>
```
#### Sync DNS Records (Idempotent)
Perfect for CI/CD - ensures DNS records match desired state.
```rust
use lmrc_cloudflare::dns::DnsRecordBuilder;
let desired_records = vec![
DnsRecordBuilder::new()
.name("api.example.com")
.record_type(RecordType::A)
.content("192.0.2.1")
.proxied(true),
];
// Dry run to see changes
let changes = client.dns()
.sync_records("zone_id")
.records(desired_records.clone())
.dry_run(true)
.send()
.await?;
// Apply changes
let changes = client.dns()
.sync_records("zone_id")
.records(desired_records)
.dry_run(false)
.send()
.await?;
```
### Types
#### `RecordType`
DNS record types:
- `RecordType::A` - IPv4 address
- `RecordType::AAAA` - IPv6 address
- `RecordType::CNAME` - Canonical name
- `RecordType::MX` - Mail exchange
- `RecordType::TXT` - Text record
- `RecordType::SRV` - Service locator
- `RecordType::NS` - Name server
- `RecordType::CAA` - Certificate authority authorization
- `RecordType::PTR` - Pointer record
#### `DnsRecord`
Response type containing full DNS record information.
**Fields:**
- `id: String` - Record ID
- `record_type: String` - Record type
- `name: String` - Record name
- `content: String` - Record content
- `proxied: bool` - Whether proxied through Cloudflare
- `ttl: u32` - Time to live
- `zone_id: String` - Zone ID
- `zone_name: String` - Zone name
- `created_on: Option<String>` - Creation timestamp
- `modified_on: Option<String>` - Modification timestamp
- `comment: Option<String>` - Record comment
- `priority: Option<u16>` - Priority (MX, SRV)
#### `DnsRecordBuilder`
Builder for creating DNS record specifications.
---
## Zone Service
Access via `client.zones()`.
### Operations
#### List Zones
```rust
let zones = client.zones()
.list()
.name("example.com") // optional filter
.status("active") // optional filter
.page(1) // optional pagination
.per_page(50) // optional pagination
.send()
.await?;
```
#### Get Zone
```rust
let zone = client.zones()
.get("zone_id")
.await?;
```
#### Find Zone by Name
```rust
let zone = client.zones()
.find_by_name("example.com")
.await?; // Returns Option<Zone>
```
#### Get Zone ID
Convenience method that combines find and ID extraction.
```rust
let zone_id = client.zones()
.get_zone_id("example.com")
.await?; // Returns Result<String>
```
### Types
#### `Zone`
**Fields:**
- `id: String` - Zone ID
- `name: String` - Zone name (domain)
- `status: String` - Zone status
- `paused: bool` - Whether zone is paused
- `zone_type: String` - Zone type (full, partial)
- `development_mode: i32` - Development mode status
- `name_servers: Vec<String>` - Assigned name servers
- `original_name_servers: Vec<String>` - Original name servers
- `created_on: Option<String>` - Creation timestamp
- `modified_on: Option<String>` - Modification timestamp
- `activated_on: Option<String>` - Activation timestamp
---
## Cache Service
Access via `client.cache()`.
### Operations
#### Purge Everything
**Warning:** Purges all cached content for a zone.
```rust
let result = client.cache()
.purge_everything("zone_id")
.await?;
```
#### Purge URLs
Purge up to 30 specific URLs.
```rust
let result = client.cache()
.purge_urls("zone_id")
.urls(vec![
"https://example.com/page1",
"https://example.com/page2",
])
.send()
.await?;
// Or add URLs individually
let result = client.cache()
.purge_urls("zone_id")
.add_url("https://example.com/page1")
.add_url("https://example.com/page2")
.send()
.await?;
```
#### Purge by Tags
Purge cache by tags (Enterprise plan only). Up to 30 tags.
```rust
let result = client.cache()
.purge_tags("zone_id")
.tags(vec!["product", "blog"])
.send()
.await?;
```
#### Purge by Hosts
Purge cache by hosts (Enterprise plan only). Up to 30 hosts.
```rust
let result = client.cache()
.purge_hosts("zone_id")
.hosts(vec!["www.example.com", "api.example.com"])
.send()
.await?;
```
#### Purge by Prefixes
Purge cache by URL prefixes (Enterprise plan only). Up to 30 prefixes.
```rust
let result = client.cache()
.purge_prefixes("zone_id")
.prefixes(vec!["example.com/images/", "example.com/videos/"])
.send()
.await?;
```
### Types
#### `PurgeResponse`
**Fields:**
- `id: String` - Purge operation ID
---
## Error Handling
### `Error`
The main error type with detailed variants.
```rust
use lmrc_cloudflare::Error;
match some_operation().await {
Ok(result) => { /* handle success */ }
Err(Error::NotFound(msg)) => { /* resource not found */ }
Err(Error::Unauthorized(msg)) => { /* auth failed */ }
Err(Error::RateLimited { retry_after }) => { /* rate limited */ }
Err(Error::Api(api_error)) => { /* API error */ }
Err(Error::Http(e)) => { /* HTTP error */ }
Err(Error::Json(e)) => { /* JSON error */ }
Err(Error::InvalidInput(msg)) => { /* invalid input */ }
}
```
#### Variants
- `Http(reqwest::Error)` - HTTP request failed
- `Api(ApiError)` - Cloudflare API returned an error
- `Json(serde_json::Error)` - JSON serialization failed
- `NotFound(String)` - Resource not found
- `InvalidInput(String)` - Invalid input or configuration
- `Unauthorized(String)` - Authentication failed
- `RateLimited { retry_after: Option<u64> }` - Rate limited
### `ApiError`
Detailed API error information.
**Fields:**
- `status: u16` - HTTP status code
- `code: Option<i32>` - Error code from Cloudflare
- `message: String` - Error message
- `body: String` - Full response body
### `Result<T>`
Type alias: `Result<T, Error>`
---
## Types
### Common Types
#### `Change<T>`
Represents a change in sync operations.
**Fields:**
- `action: ChangeAction` - The action type
- `current: Option<T>` - Current state
- `desired: Option<T>` - Desired state
- `description: String` - Human-readable description
**Methods:**
- `create(desired: T, description: String) -> Self`
- `update(current: T, desired: T, description: String) -> Self`
- `no_change(current: T, description: String) -> Self`
#### `ChangeAction`
Enumeration of possible change actions.
**Variants:**
- `Create` - Create new resource
- `Update` - Update existing resource
- `Delete` - Delete resource
- `NoChange` - No change needed
**Methods:**
- `is_mutating(&self) -> bool` - Check if action modifies resources
---
## Usage Patterns
### CI/CD Pattern
```rust
// 1. Create client from environment
let client = CloudflareClient::new(env::var("CLOUDFLARE_API_TOKEN")?)?;
// 2. Get zone ID
let zone_id = client.zones().get_zone_id("example.com").await?;
// 3. Define desired state
let records = vec![/* ... */];
// 4. Sync with dry run first
let changes = client.dns()
.sync_records(&zone_id)
.records(records.clone())
.dry_run(true)
.send()
.await?;
// 5. Review changes and apply
if user_approves(&changes) {
client.dns()
.sync_records(&zone_id)
.records(records)
.dry_run(false)
.send()
.await?;
}
```
### Error Recovery Pattern
```rust
use lmrc_cloudflare::Error;
match client.dns().create_record(&zone_id)
.name("api.example.com")
.record_type(RecordType::A)
.content("192.0.2.1")
.send()
.await
{
Ok(record) => println!("Created: {}", record.id),
Err(Error::RateLimited { retry_after: Some(seconds) }) => {
tokio::time::sleep(Duration::from_secs(seconds)).await;
// Retry operation
}
Err(e) => return Err(e.into()),
}
```
---
## Async Runtime
This library requires an async runtime. All examples use `tokio`:
```toml
[dependencies]
cloudflare-client = "0.1"
tokio = { version = "1", features = ["full"] }
```
```rust
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Your code here
Ok(())
}
```
---
## Notes
- All API operations are async and require `.await`
- Builder methods can be chained for ergonomic API
- Operations are idempotent where possible (especially sync operations)
- Rate limiting is automatically detected and reported via errors
- All strings are owned (`String`) for simplicity and safety
- The client can be cloned cheaply (uses `Arc` internally via `reqwest::Client`)