mirror of
https://github.com/ksyasuda/dotfiles.git
synced 2026-03-20 18:11:27 -07:00
update skills
This commit is contained in:
65
.agents/skills/cloudflare-deploy/references/api/README.md
Normal file
65
.agents/skills/cloudflare-deploy/references/api/README.md
Normal file
@@ -0,0 +1,65 @@
|
||||
# Cloudflare API Integration
|
||||
|
||||
Guide for working with Cloudflare's REST API - authentication, SDK usage, common patterns, and troubleshooting.
|
||||
|
||||
## Quick Decision Tree
|
||||
|
||||
```
|
||||
How are you calling the Cloudflare API?
|
||||
├─ From Workers runtime → Use bindings, not REST API (see ../bindings/)
|
||||
├─ Server-side (Node/Python/Go) → Official SDK (see api.md)
|
||||
├─ CLI/scripts → Wrangler or curl (see configuration.md)
|
||||
├─ Infrastructure-as-code → See ../pulumi/ or ../terraform/
|
||||
└─ One-off requests → curl examples (see api.md)
|
||||
```
|
||||
|
||||
## SDK Selection
|
||||
|
||||
| Language | Package | Best For | Default Retries |
|
||||
|----------|---------|----------|-----------------|
|
||||
| TypeScript | `cloudflare` | Node.js, Bun, Next.js, Workers | 2 |
|
||||
| Python | `cloudflare` | FastAPI, Django, scripts | 2 |
|
||||
| Go | `cloudflare-go/v4` | CLI tools, microservices | 10 |
|
||||
|
||||
All SDKs are Stainless-generated from OpenAPI spec (consistent APIs).
|
||||
|
||||
## Authentication Methods
|
||||
|
||||
| Method | Security | Use Case | Scope |
|
||||
|--------|----------|----------|-------|
|
||||
| **API Token** ✓ | Scoped, rotatable | Production | Per-zone or account |
|
||||
| API Key + Email | Full account access | Legacy only | Everything |
|
||||
| User Service Key | Limited | Origin CA certs only | Origin CA |
|
||||
|
||||
**Always use API tokens** for new projects.
|
||||
|
||||
## Rate Limits
|
||||
|
||||
| Limit | Value |
|
||||
|-------|-------|
|
||||
| Per user/token | 1200 requests / 5 minutes |
|
||||
| Per IP | 200 requests / second |
|
||||
| GraphQL | 320 / 5 minutes (cost-based) |
|
||||
|
||||
## Reading Order
|
||||
|
||||
| Task | Files to Read |
|
||||
|------|---------------|
|
||||
| Initialize SDK client | api.md |
|
||||
| Configure auth/timeout/retry | configuration.md |
|
||||
| Find usage patterns | patterns.md |
|
||||
| Debug errors/rate limits | gotchas.md |
|
||||
| Product-specific APIs | ../workers/, ../r2/, ../kv/, etc. |
|
||||
|
||||
## In This Reference
|
||||
|
||||
- **[api.md](api.md)** - SDK client initialization, pagination, error handling, examples
|
||||
- **[configuration.md](configuration.md)** - Environment variables, SDK config, Wrangler setup
|
||||
- **[patterns.md](patterns.md)** - Real-world patterns, batch operations, workflows
|
||||
- **[gotchas.md](gotchas.md)** - Rate limits, SDK-specific issues, troubleshooting
|
||||
|
||||
## See Also
|
||||
|
||||
- [Cloudflare API Docs](https://developers.cloudflare.com/api/)
|
||||
- [Bindings Reference](../bindings/) - Workers runtime bindings (preferred over REST API)
|
||||
- [Wrangler Reference](../wrangler/) - CLI tool for Cloudflare development
|
||||
204
.agents/skills/cloudflare-deploy/references/api/api.md
Normal file
204
.agents/skills/cloudflare-deploy/references/api/api.md
Normal file
@@ -0,0 +1,204 @@
|
||||
# API Reference
|
||||
|
||||
## Client Initialization
|
||||
|
||||
### TypeScript
|
||||
|
||||
```typescript
|
||||
import Cloudflare from 'cloudflare';
|
||||
|
||||
const client = new Cloudflare({
|
||||
apiToken: process.env.CLOUDFLARE_API_TOKEN,
|
||||
});
|
||||
```
|
||||
|
||||
### Python
|
||||
|
||||
```python
|
||||
from cloudflare import Cloudflare
|
||||
|
||||
client = Cloudflare(api_token=os.environ.get("CLOUDFLARE_API_TOKEN"))
|
||||
|
||||
# For async:
|
||||
from cloudflare import AsyncCloudflare
|
||||
client = AsyncCloudflare(api_token=os.environ["CLOUDFLARE_API_TOKEN"])
|
||||
```
|
||||
|
||||
### Go
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/cloudflare/cloudflare-go/v4"
|
||||
"github.com/cloudflare/cloudflare-go/v4/option"
|
||||
)
|
||||
|
||||
client := cloudflare.NewClient(
|
||||
option.WithAPIToken(os.Getenv("CLOUDFLARE_API_TOKEN")),
|
||||
)
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
### API Token (Recommended)
|
||||
|
||||
**Create token**: Dashboard → My Profile → API Tokens → Create Token
|
||||
|
||||
```bash
|
||||
export CLOUDFLARE_API_TOKEN='your-token-here'
|
||||
|
||||
curl "https://api.cloudflare.com/client/v4/zones" \
|
||||
--header "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
|
||||
```
|
||||
|
||||
**Token scopes**: Always use minimal permissions (zone-specific, time-limited).
|
||||
|
||||
### API Key (Legacy)
|
||||
|
||||
```bash
|
||||
curl "https://api.cloudflare.com/client/v4/zones" \
|
||||
--header "X-Auth-Email: user@example.com" \
|
||||
--header "X-Auth-Key: $CLOUDFLARE_API_KEY"
|
||||
```
|
||||
|
||||
**Not recommended:** Full account access, cannot scope permissions.
|
||||
|
||||
## Auto-Pagination
|
||||
|
||||
All SDKs support automatic pagination for list operations.
|
||||
|
||||
```typescript
|
||||
// TypeScript: for await...of
|
||||
for await (const zone of client.zones.list()) {
|
||||
console.log(zone.id);
|
||||
}
|
||||
```
|
||||
|
||||
```python
|
||||
# Python: iterator protocol
|
||||
for zone in client.zones.list():
|
||||
print(zone.id)
|
||||
```
|
||||
|
||||
```go
|
||||
// Go: ListAutoPaging
|
||||
iter := client.Zones.ListAutoPaging(ctx, cloudflare.ZoneListParams{})
|
||||
for iter.Next() {
|
||||
zone := iter.Current()
|
||||
fmt.Println(zone.ID)
|
||||
}
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
```typescript
|
||||
try {
|
||||
const zone = await client.zones.get({ zone_id: 'xxx' });
|
||||
} catch (err) {
|
||||
if (err instanceof Cloudflare.NotFoundError) {
|
||||
// 404
|
||||
} else if (err instanceof Cloudflare.RateLimitError) {
|
||||
// 429 - SDK auto-retries with backoff
|
||||
} else if (err instanceof Cloudflare.APIError) {
|
||||
console.log(err.status, err.message);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Common Error Types:**
|
||||
- `AuthenticationError` (401) - Invalid token
|
||||
- `PermissionDeniedError` (403) - Insufficient scope
|
||||
- `NotFoundError` (404) - Resource not found
|
||||
- `RateLimitError` (429) - Rate limit exceeded
|
||||
- `InternalServerError` (≥500) - Cloudflare error
|
||||
|
||||
## Zone Management
|
||||
|
||||
```typescript
|
||||
// List zones
|
||||
const zones = await client.zones.list({
|
||||
account: { id: 'account-id' },
|
||||
status: 'active',
|
||||
});
|
||||
|
||||
// Create zone
|
||||
const zone = await client.zones.create({
|
||||
account: { id: 'account-id' },
|
||||
name: 'example.com',
|
||||
type: 'full', // or 'partial'
|
||||
});
|
||||
|
||||
// Update zone
|
||||
await client.zones.edit('zone-id', {
|
||||
paused: false,
|
||||
});
|
||||
|
||||
// Delete zone
|
||||
await client.zones.delete('zone-id');
|
||||
```
|
||||
|
||||
```go
|
||||
// Go: requires cloudflare.F() wrapper
|
||||
zone, err := client.Zones.New(ctx, cloudflare.ZoneNewParams{
|
||||
Account: cloudflare.F(cloudflare.ZoneNewParamsAccount{
|
||||
ID: cloudflare.F("account-id"),
|
||||
}),
|
||||
Name: cloudflare.F("example.com"),
|
||||
Type: cloudflare.F(cloudflare.ZoneNewParamsTypeFull),
|
||||
})
|
||||
```
|
||||
|
||||
## DNS Management
|
||||
|
||||
```typescript
|
||||
// Create DNS record
|
||||
await client.dns.records.create({
|
||||
zone_id: 'zone-id',
|
||||
type: 'A',
|
||||
name: 'subdomain.example.com',
|
||||
content: '192.0.2.1',
|
||||
ttl: 1, // auto
|
||||
proxied: true, // Orange cloud
|
||||
});
|
||||
|
||||
// List DNS records (with auto-pagination)
|
||||
for await (const record of client.dns.records.list({
|
||||
zone_id: 'zone-id',
|
||||
type: 'A',
|
||||
})) {
|
||||
console.log(record.name, record.content);
|
||||
}
|
||||
|
||||
// Update DNS record
|
||||
await client.dns.records.update({
|
||||
zone_id: 'zone-id',
|
||||
dns_record_id: 'record-id',
|
||||
type: 'A',
|
||||
name: 'subdomain.example.com',
|
||||
content: '203.0.113.1',
|
||||
proxied: true,
|
||||
});
|
||||
|
||||
// Delete DNS record
|
||||
await client.dns.records.delete({
|
||||
zone_id: 'zone-id',
|
||||
dns_record_id: 'record-id',
|
||||
});
|
||||
```
|
||||
|
||||
```python
|
||||
# Python example
|
||||
client.dns.records.create(
|
||||
zone_id="zone-id",
|
||||
type="A",
|
||||
name="subdomain.example.com",
|
||||
content="192.0.2.1",
|
||||
ttl=1,
|
||||
proxied=True,
|
||||
)
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [configuration.md](./configuration.md) - SDK configuration, environment variables
|
||||
- [patterns.md](./patterns.md) - Real-world patterns and workflows
|
||||
- [gotchas.md](./gotchas.md) - Rate limits, troubleshooting
|
||||
160
.agents/skills/cloudflare-deploy/references/api/configuration.md
Normal file
160
.agents/skills/cloudflare-deploy/references/api/configuration.md
Normal file
@@ -0,0 +1,160 @@
|
||||
# Configuration
|
||||
|
||||
## Environment Variables
|
||||
|
||||
### Set Variables
|
||||
|
||||
| Platform | Command |
|
||||
|----------|---------|
|
||||
| Linux/macOS | `export CLOUDFLARE_API_TOKEN='token'` |
|
||||
| PowerShell | `$env:CLOUDFLARE_API_TOKEN = 'token'` |
|
||||
| Windows CMD | `set CLOUDFLARE_API_TOKEN=token` |
|
||||
|
||||
**Security:** Never commit tokens. Use `.env` files (gitignored) or secret managers.
|
||||
|
||||
### .env File Pattern
|
||||
|
||||
```bash
|
||||
# .env (add to .gitignore)
|
||||
CLOUDFLARE_API_TOKEN=your-token-here
|
||||
CLOUDFLARE_ACCOUNT_ID=your-account-id
|
||||
```
|
||||
|
||||
```typescript
|
||||
// TypeScript
|
||||
import 'dotenv/config';
|
||||
|
||||
const client = new Cloudflare({
|
||||
apiToken: process.env.CLOUDFLARE_API_TOKEN,
|
||||
});
|
||||
```
|
||||
|
||||
```python
|
||||
# Python
|
||||
from dotenv import load_dotenv
|
||||
load_dotenv()
|
||||
|
||||
client = Cloudflare(api_token=os.environ["CLOUDFLARE_API_TOKEN"])
|
||||
```
|
||||
|
||||
## SDK Configuration
|
||||
|
||||
### TypeScript
|
||||
|
||||
```typescript
|
||||
const client = new Cloudflare({
|
||||
apiToken: process.env.CLOUDFLARE_API_TOKEN,
|
||||
timeout: 120000, // 2 min (default 60s), in milliseconds
|
||||
maxRetries: 5, // default 2
|
||||
baseURL: 'https://...', // proxy (rare)
|
||||
});
|
||||
|
||||
// Per-request overrides
|
||||
await client.zones.get(
|
||||
{ zone_id: 'zone-id' },
|
||||
{ timeout: 5000, maxRetries: 0 }
|
||||
);
|
||||
```
|
||||
|
||||
### Python
|
||||
|
||||
```python
|
||||
client = Cloudflare(
|
||||
api_token=os.environ["CLOUDFLARE_API_TOKEN"],
|
||||
timeout=120, # seconds (default 60)
|
||||
max_retries=5, # default 2
|
||||
base_url="https://...", # proxy (rare)
|
||||
)
|
||||
|
||||
# Per-request overrides
|
||||
client.with_options(timeout=5, max_retries=0).zones.get(zone_id="zone-id")
|
||||
```
|
||||
|
||||
### Go
|
||||
|
||||
```go
|
||||
client := cloudflare.NewClient(
|
||||
option.WithAPIToken(os.Getenv("CLOUDFLARE_API_TOKEN")),
|
||||
option.WithMaxRetries(5), // default 10 (higher than TS/Python)
|
||||
option.WithRequestTimeout(2 * time.Minute), // default 60s
|
||||
option.WithBaseURL("https://..."), // proxy (rare)
|
||||
)
|
||||
|
||||
// Per-request overrides
|
||||
client.Zones.Get(ctx, "zone-id", option.WithMaxRetries(0))
|
||||
```
|
||||
|
||||
## Configuration Options
|
||||
|
||||
| Option | TypeScript | Python | Go | Default |
|
||||
|--------|-----------|--------|-----|---------|
|
||||
| Timeout | `timeout` (ms) | `timeout` (s) | `WithRequestTimeout` | 60s |
|
||||
| Retries | `maxRetries` | `max_retries` | `WithMaxRetries` | 2 (Go: 10) |
|
||||
| Base URL | `baseURL` | `base_url` | `WithBaseURL` | api.cloudflare.com |
|
||||
|
||||
**Note:** Go SDK has higher default retries (10) than TypeScript/Python (2).
|
||||
|
||||
## Timeout Configuration
|
||||
|
||||
**When to increase:**
|
||||
- Large zone transfers
|
||||
- Bulk DNS operations
|
||||
- Worker script uploads
|
||||
|
||||
```typescript
|
||||
const client = new Cloudflare({
|
||||
timeout: 300000, // 5 minutes
|
||||
});
|
||||
```
|
||||
|
||||
## Retry Configuration
|
||||
|
||||
**When to increase:** Rate-limit-heavy workflows, flaky network
|
||||
|
||||
**When to decrease:** Fast-fail requirements, user-facing requests
|
||||
|
||||
```typescript
|
||||
// Increase retries for batch operations
|
||||
const client = new Cloudflare({ maxRetries: 10 });
|
||||
|
||||
// Disable retries for fast-fail
|
||||
const fastClient = new Cloudflare({ maxRetries: 0 });
|
||||
```
|
||||
|
||||
## Wrangler CLI Integration
|
||||
|
||||
```bash
|
||||
# Configure authentication
|
||||
wrangler login
|
||||
# Or
|
||||
export CLOUDFLARE_API_TOKEN='token'
|
||||
|
||||
# Common commands that use API
|
||||
wrangler deploy # Uploads worker via API
|
||||
wrangler kv:key put # KV operations
|
||||
wrangler r2 bucket create # R2 operations
|
||||
wrangler d1 execute # D1 operations
|
||||
wrangler pages deploy # Pages operations
|
||||
|
||||
# Get API configuration
|
||||
wrangler whoami # Shows authenticated user
|
||||
```
|
||||
|
||||
### wrangler.toml
|
||||
|
||||
```toml
|
||||
name = "my-worker"
|
||||
main = "src/index.ts"
|
||||
compatibility_date = "2024-01-01"
|
||||
account_id = "your-account-id"
|
||||
|
||||
# Can also use env vars:
|
||||
# CLOUDFLARE_ACCOUNT_ID
|
||||
# CLOUDFLARE_API_TOKEN
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [api.md](./api.md) - Client initialization, authentication
|
||||
- [gotchas.md](./gotchas.md) - Rate limits, timeout errors
|
||||
- [Wrangler Reference](../wrangler/) - CLI tool details
|
||||
225
.agents/skills/cloudflare-deploy/references/api/gotchas.md
Normal file
225
.agents/skills/cloudflare-deploy/references/api/gotchas.md
Normal file
@@ -0,0 +1,225 @@
|
||||
# Gotchas & Troubleshooting
|
||||
|
||||
## Rate Limits & 429 Errors
|
||||
|
||||
**Actual Limits:**
|
||||
- **1200 requests / 5 minutes** per user/token (global)
|
||||
- **200 requests / second** per IP address
|
||||
- **GraphQL: 320 / 5 minutes** (cost-based)
|
||||
|
||||
**SDK Behavior:**
|
||||
- Auto-retry with exponential backoff (default 2 retries, Go: 10)
|
||||
- Respects `Retry-After` header
|
||||
- Throws `RateLimitError` after exhausting retries
|
||||
|
||||
**Solution:**
|
||||
|
||||
```typescript
|
||||
// Increase retries for rate-limit-heavy workflows
|
||||
const client = new Cloudflare({ maxRetries: 5 });
|
||||
|
||||
// Add application-level throttling
|
||||
import pLimit from 'p-limit';
|
||||
const limit = pLimit(10); // Max 10 concurrent requests
|
||||
```
|
||||
|
||||
## SDK-Specific Issues
|
||||
|
||||
### Go: Required Field Wrapper
|
||||
|
||||
**Problem:** Go SDK requires `cloudflare.F()` wrapper for optional fields.
|
||||
|
||||
```go
|
||||
// ❌ WRONG - Won't compile or send field
|
||||
client.Zones.New(ctx, cloudflare.ZoneNewParams{
|
||||
Name: "example.com",
|
||||
})
|
||||
|
||||
// ✅ CORRECT
|
||||
client.Zones.New(ctx, cloudflare.ZoneNewParams{
|
||||
Name: cloudflare.F("example.com"),
|
||||
Account: cloudflare.F(cloudflare.ZoneNewParamsAccount{
|
||||
ID: cloudflare.F("account-id"),
|
||||
}),
|
||||
})
|
||||
```
|
||||
|
||||
**Why:** Distinguishes between zero value, null, and omitted fields.
|
||||
|
||||
### Python: Async vs Sync Clients
|
||||
|
||||
**Problem:** Using sync client in async context or vice versa.
|
||||
|
||||
```python
|
||||
# ❌ WRONG - Can't await sync client
|
||||
from cloudflare import Cloudflare
|
||||
client = Cloudflare()
|
||||
await client.zones.list() # TypeError
|
||||
|
||||
# ✅ CORRECT - Use AsyncCloudflare
|
||||
from cloudflare import AsyncCloudflare
|
||||
client = AsyncCloudflare()
|
||||
await client.zones.list()
|
||||
```
|
||||
|
||||
## Token Permission Errors (403)
|
||||
|
||||
**Problem:** API returns 403 Forbidden despite valid token.
|
||||
|
||||
**Cause:** Token lacks required permissions (scope).
|
||||
|
||||
**Scopes Required:**
|
||||
|
||||
| Operation | Required Scope |
|
||||
|-----------|----------------|
|
||||
| List zones | Zone:Read (zone-level or account-level) |
|
||||
| Create zone | Zone:Edit (account-level) |
|
||||
| Edit DNS | DNS:Edit (zone-level) |
|
||||
| Deploy Worker | Workers Script:Edit (account-level) |
|
||||
| Read KV | Workers KV Storage:Read |
|
||||
| Write KV | Workers KV Storage:Edit |
|
||||
|
||||
**Solution:** Re-create token with correct permissions in Dashboard → My Profile → API Tokens.
|
||||
|
||||
## Pagination Truncation
|
||||
|
||||
**Problem:** Only getting first 20 results (default page size).
|
||||
|
||||
**Solution:** Use auto-pagination iterators.
|
||||
|
||||
```typescript
|
||||
// ❌ WRONG - Only first page (20 items)
|
||||
const page = await client.zones.list();
|
||||
|
||||
// ✅ CORRECT - All results
|
||||
const zones = [];
|
||||
for await (const zone of client.zones.list()) {
|
||||
zones.push(zone);
|
||||
}
|
||||
```
|
||||
|
||||
## Workers Subrequests
|
||||
|
||||
**Problem:** Rate limit hit faster than expected in Workers.
|
||||
|
||||
**Cause:** Workers subrequests count as separate API calls.
|
||||
|
||||
**Solution:** Use bindings instead of REST API in Workers (see ../bindings/).
|
||||
|
||||
```typescript
|
||||
// ❌ WRONG - REST API in Workers (counts against rate limit)
|
||||
const client = new Cloudflare({ apiToken: env.CLOUDFLARE_API_TOKEN });
|
||||
const zones = await client.zones.list();
|
||||
|
||||
// ✅ CORRECT - Use bindings (no rate limit)
|
||||
// Access via env.MY_BINDING
|
||||
```
|
||||
|
||||
## Authentication Errors (401)
|
||||
|
||||
**Problem:** "Authentication failed" or "Invalid token"
|
||||
|
||||
**Causes:**
|
||||
- Token expired
|
||||
- Token deleted/revoked
|
||||
- Token not set in environment
|
||||
- Wrong token format
|
||||
|
||||
**Solution:**
|
||||
|
||||
```typescript
|
||||
// Verify token is set
|
||||
if (!process.env.CLOUDFLARE_API_TOKEN) {
|
||||
throw new Error('CLOUDFLARE_API_TOKEN not set');
|
||||
}
|
||||
|
||||
// Test token
|
||||
const user = await client.user.tokens.verify();
|
||||
console.log('Token valid:', user.status);
|
||||
```
|
||||
|
||||
## Timeout Errors
|
||||
|
||||
**Problem:** Request times out (default 60s).
|
||||
|
||||
**Cause:** Large operations (bulk DNS, zone transfers).
|
||||
|
||||
**Solution:** Increase timeout or split operations.
|
||||
|
||||
```typescript
|
||||
// Increase timeout
|
||||
const client = new Cloudflare({
|
||||
timeout: 300000, // 5 minutes
|
||||
});
|
||||
|
||||
// Or split operations
|
||||
const batchSize = 100;
|
||||
for (let i = 0; i < records.length; i += batchSize) {
|
||||
const batch = records.slice(i, i + batchSize);
|
||||
await processBatch(batch);
|
||||
}
|
||||
```
|
||||
|
||||
## Zone Not Found (404)
|
||||
|
||||
**Problem:** Zone ID valid but returns 404.
|
||||
|
||||
**Causes:**
|
||||
- Zone not in account associated with token
|
||||
- Zone deleted
|
||||
- Wrong zone ID format
|
||||
|
||||
**Solution:**
|
||||
|
||||
```typescript
|
||||
// List all zones to find correct ID
|
||||
for await (const zone of client.zones.list()) {
|
||||
console.log(zone.id, zone.name);
|
||||
}
|
||||
```
|
||||
|
||||
## Limits Reference
|
||||
|
||||
| Resource/Limit | Value | Notes |
|
||||
|----------------|-------|-------|
|
||||
| API rate limit | 1200/5min | Per user/token |
|
||||
| IP rate limit | 200/sec | Per IP |
|
||||
| GraphQL rate limit | 320/5min | Cost-based |
|
||||
| Parallel requests (recommended) | < 10 | Avoid overwhelming API |
|
||||
| Default page size | 20 | Use auto-pagination |
|
||||
| Max page size | 50 | Some endpoints |
|
||||
|
||||
## Best Practices
|
||||
|
||||
**Security:**
|
||||
- Never commit tokens
|
||||
- Use minimal permissions
|
||||
- Rotate tokens regularly
|
||||
- Set token expiration
|
||||
|
||||
**Performance:**
|
||||
- Batch operations
|
||||
- Use pagination wisely
|
||||
- Cache responses
|
||||
- Handle rate limits
|
||||
|
||||
**Code Organization:**
|
||||
|
||||
```typescript
|
||||
// Create reusable client instance
|
||||
export const cfClient = new Cloudflare({
|
||||
apiToken: process.env.CLOUDFLARE_API_TOKEN,
|
||||
maxRetries: 5,
|
||||
});
|
||||
|
||||
// Wrap common operations
|
||||
export async function getZoneDetails(zoneId: string) {
|
||||
return await cfClient.zones.get({ zone_id: zoneId });
|
||||
}
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [api.md](./api.md) - Error types, authentication
|
||||
- [configuration.md](./configuration.md) - Timeout/retry configuration
|
||||
- [patterns.md](./patterns.md) - Error handling patterns
|
||||
204
.agents/skills/cloudflare-deploy/references/api/patterns.md
Normal file
204
.agents/skills/cloudflare-deploy/references/api/patterns.md
Normal file
@@ -0,0 +1,204 @@
|
||||
# Common Patterns
|
||||
|
||||
## List All with Auto-Pagination
|
||||
|
||||
**Problem:** API returns paginated results. Default page size is 20.
|
||||
|
||||
**Solution:** Use SDK auto-pagination to iterate all results.
|
||||
|
||||
```typescript
|
||||
// TypeScript
|
||||
for await (const zone of client.zones.list()) {
|
||||
console.log(zone.name);
|
||||
}
|
||||
```
|
||||
|
||||
```python
|
||||
# Python
|
||||
for zone in client.zones.list():
|
||||
print(zone.name)
|
||||
```
|
||||
|
||||
```go
|
||||
// Go
|
||||
iter := client.Zones.ListAutoPaging(ctx, cloudflare.ZoneListParams{})
|
||||
for iter.Next() {
|
||||
fmt.Println(iter.Current().Name)
|
||||
}
|
||||
```
|
||||
|
||||
## Error Handling with Retry
|
||||
|
||||
**Problem:** Rate limits (429) and transient errors need retry.
|
||||
|
||||
**Solution:** SDKs auto-retry with exponential backoff. Customize as needed.
|
||||
|
||||
```typescript
|
||||
// Increase retries for rate-limit-heavy operations
|
||||
const client = new Cloudflare({ maxRetries: 5 });
|
||||
|
||||
try {
|
||||
const zone = await client.zones.create({ /* ... */ });
|
||||
} catch (err) {
|
||||
if (err instanceof Cloudflare.RateLimitError) {
|
||||
// Already retried 5 times with backoff
|
||||
const retryAfter = err.headers['retry-after'];
|
||||
console.log(`Rate limited. Retry after ${retryAfter}s`);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Batch Parallel Operations
|
||||
|
||||
**Problem:** Need to create multiple resources quickly.
|
||||
|
||||
**Solution:** Use `Promise.all()` for parallel requests (respect rate limits).
|
||||
|
||||
```typescript
|
||||
// Create multiple DNS records in parallel
|
||||
const records = ['www', 'api', 'cdn'].map(subdomain =>
|
||||
client.dns.records.create({
|
||||
zone_id: 'zone-id',
|
||||
type: 'A',
|
||||
name: `${subdomain}.example.com`,
|
||||
content: '192.0.2.1',
|
||||
})
|
||||
);
|
||||
await Promise.all(records);
|
||||
```
|
||||
|
||||
**Controlled concurrency** (avoid rate limits):
|
||||
|
||||
```typescript
|
||||
import pLimit from 'p-limit';
|
||||
const limit = pLimit(10); // Max 10 concurrent
|
||||
|
||||
const subdomains = ['www', 'api', 'cdn', /* many more */];
|
||||
const records = subdomains.map(subdomain =>
|
||||
limit(() => client.dns.records.create({
|
||||
zone_id: 'zone-id',
|
||||
type: 'A',
|
||||
name: `${subdomain}.example.com`,
|
||||
content: '192.0.2.1',
|
||||
}))
|
||||
);
|
||||
await Promise.all(records);
|
||||
```
|
||||
|
||||
## Zone CRUD Workflow
|
||||
|
||||
```typescript
|
||||
// Create
|
||||
const zone = await client.zones.create({
|
||||
account: { id: 'account-id' },
|
||||
name: 'example.com',
|
||||
type: 'full',
|
||||
});
|
||||
|
||||
// Read
|
||||
const fetched = await client.zones.get({ zone_id: zone.id });
|
||||
|
||||
// Update
|
||||
await client.zones.edit(zone.id, { paused: false });
|
||||
|
||||
// Delete
|
||||
await client.zones.delete(zone.id);
|
||||
```
|
||||
|
||||
## DNS Bulk Update
|
||||
|
||||
```typescript
|
||||
// Fetch all A records
|
||||
const records = [];
|
||||
for await (const record of client.dns.records.list({
|
||||
zone_id: 'zone-id',
|
||||
type: 'A',
|
||||
})) {
|
||||
records.push(record);
|
||||
}
|
||||
|
||||
// Update all to new IP
|
||||
await Promise.all(records.map(record =>
|
||||
client.dns.records.update({
|
||||
zone_id: 'zone-id',
|
||||
dns_record_id: record.id,
|
||||
type: 'A',
|
||||
name: record.name,
|
||||
content: '203.0.113.1', // New IP
|
||||
proxied: record.proxied,
|
||||
ttl: record.ttl,
|
||||
})
|
||||
));
|
||||
```
|
||||
|
||||
## Filter and Collect Results
|
||||
|
||||
```typescript
|
||||
// Find all proxied A records
|
||||
const proxiedRecords = [];
|
||||
for await (const record of client.dns.records.list({
|
||||
zone_id: 'zone-id',
|
||||
type: 'A',
|
||||
})) {
|
||||
if (record.proxied) {
|
||||
proxiedRecords.push(record);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Error Recovery Pattern
|
||||
|
||||
```typescript
|
||||
async function createZoneWithRetry(name: string, maxAttempts = 3) {
|
||||
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
||||
try {
|
||||
return await client.zones.create({
|
||||
account: { id: 'account-id' },
|
||||
name,
|
||||
type: 'full',
|
||||
});
|
||||
} catch (err) {
|
||||
if (err instanceof Cloudflare.RateLimitError && attempt < maxAttempts) {
|
||||
const retryAfter = parseInt(err.headers['retry-after'] || '5');
|
||||
console.log(`Rate limited, waiting ${retryAfter}s (retry ${attempt}/${maxAttempts})`);
|
||||
await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Conditional Update Pattern
|
||||
|
||||
```typescript
|
||||
// Only update if zone is active
|
||||
const zone = await client.zones.get({ zone_id: 'zone-id' });
|
||||
if (zone.status === 'active') {
|
||||
await client.zones.edit(zone.id, { paused: false });
|
||||
}
|
||||
```
|
||||
|
||||
## Batch with Error Handling
|
||||
|
||||
```typescript
|
||||
// Process multiple zones, continue on errors
|
||||
const results = await Promise.allSettled(
|
||||
zoneIds.map(id => client.zones.get({ zone_id: id }))
|
||||
);
|
||||
|
||||
results.forEach((result, i) => {
|
||||
if (result.status === 'fulfilled') {
|
||||
console.log(`Zone ${i}: ${result.value.name}`);
|
||||
} else {
|
||||
console.error(`Zone ${i} failed:`, result.reason.message);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [api.md](./api.md) - SDK client initialization, basic operations
|
||||
- [gotchas.md](./gotchas.md) - Rate limits, common errors
|
||||
- [configuration.md](./configuration.md) - SDK configuration options
|
||||
Reference in New Issue
Block a user