API Reference

CDN & File Upload

Oblien CDN manages file uploads with secure, short-lived tokens. You generate tokens on your backend, then use them on your frontend to upload files directly to the CDN.

Overview

How It Works:

  1. Backend - Generate CDN token using your Client ID/Secret (server-side only)
  2. Frontend - Use token to upload files directly to CDN at https://cdn.oblien.com/api
  3. Oblien - Processes images, generates variants, and manages storage
  4. Backend - List, manage, and track user's uploaded files

What We Manage:

  • Token generation and validation (1-minute expiration)
  • File processing and image optimization
  • Automatic variant generation (thumbnails, blur, etc.)
  • File storage and CDN delivery
  • Upload history and file tracking

What You Do:

  • Generate tokens on your backend (one API call)
  • Upload files from frontend using tokens
  • (Optional) Manage and list user's files

Security: Never expose your Client ID or Client Secret in frontend code. Token generation must always happen on your backend server. Only the CDN token should be sent to the frontend.

Quick Start

1. Backend: Generate Token

// backend/api/cdn-token.js
import { OblienClient, OblienCDN } from 'oblien';

const client = new OblienClient({
  clientId: process.env.OBLIEN_CLIENT_ID,
  clientSecret: process.env.OBLIEN_CLIENT_SECRET
});

const cdn = new OblienCDN(client);

// Generate user token (upload/process permissions)
const tokenData = await cdn.generateUserToken();

// Send to frontend
res.json({
  token: tokenData.token,
  expiresIn: '1m' // Tokens expire in 1 minute
});
const response = await fetch('https://api.oblien.com/cdn/token', {
  method: 'POST',
  headers: {
    'X-Client-ID': process.env.OBLIEN_CLIENT_ID,
    'X-Client-Secret': process.env.OBLIEN_CLIENT_SECRET
  }
});

const { token } = await response.json();
curl -X POST https://api.oblien.com/cdn/token \
  -H "X-Client-ID: $OBLIEN_CLIENT_ID" \
  -H "X-Client-Secret: $OBLIEN_CLIENT_SECRET"

Response:

{
  "success": true,
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "scope": "user",
  "permissions": ["upload", "process"],
  "expiresIn": "1m"
}

2. Frontend: Upload Files

// frontend/upload.js
async function uploadFile(file) {
  // Get token from your backend
  const { token } = await fetch('/api/cdn-token').then(r => r.json());
  
  // Upload to CDN
  const formData = new FormData();
  formData.append('file', file);
  
  const response = await fetch('https://cdn.oblien.com/api/', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`
    },
    body: formData
  });
  
  const result = await response.json();
  console.log('Uploaded:', result.url);
  return result;
}
import { useState } from 'react';

function FileUpload() {
  const [uploading, setUploading] = useState(false);
  const [url, setUrl] = useState(null);
  
  const handleUpload = async (e) => {
    const file = e.target.files[0];
    if (!file) return;
    
    setUploading(true);
    
    try {
      // Get token from your backend
      const { token } = await fetch('/api/cdn-token').then(r => r.json());
      
      // Upload to CDN
      const formData = new FormData();
      formData.append('file', file);
      
      const response = await fetch('https://cdn.oblien.com/api/', {
        method: 'POST',
        headers: { 'Authorization': `Bearer ${token}` },
        body: formData
      });
      
      const result = await response.json();
      setUrl(result.url);
    } finally {
      setUploading(false);
    }
  };
  
  return (
    <div>
      <input type="file" onChange={handleUpload} disabled={uploading} />
      {url && <img src={url} alt="Uploaded" />}
    </div>
  );
}
# First, get token from your backend
TOKEN=$(curl -X POST http://your-backend.com/api/cdn-token | jq -r '.token')

# Then upload to CDN
curl -X POST https://cdn.oblien.com/api/ \
  -H "Authorization: Bearer $TOKEN" \
  -F "file=@image.jpg"

Response:

{
  "success": true,
  "url": "https://cdn.oblien.com/abc123/image.jpg",
  "filename": "1234567890-abc123-image.jpg",
  "size": 102400,
  "mime": "image/jpeg",
  "variants": [
    {
      "variant": "thumb",
      "url": "https://cdn.oblien.com/abc123/thumb/image.jpg"
    },
    {
      "variant": "medium",
      "url": "https://cdn.oblien.com/abc123/medium/image.jpg"
    },
    {
      "variant": "blur",
      "url": "https://cdn.oblien.com/abc123/blur/image.jpg"
    }
  ]
}

Token Generation

Generate User Token

User tokens have upload and process permissions. Perfect for standard file uploads.

const cdn = new OblienCDN(client);

const tokenData = await cdn.generateUserToken();

// Returns:
// {
//   success: true,
//   token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
//   scope: "user",
//   permissions: ["upload", "process"],
//   expiresIn: "1m"
// }
const response = await fetch('https://api.oblien.com/cdn/token', {
  method: 'POST',
  headers: {
    'X-Client-ID': process.env.OBLIEN_CLIENT_ID,
    'X-Client-Secret': process.env.OBLIEN_CLIENT_SECRET
  }
});

const tokenData = await response.json();
curl -X POST https://api.oblien.com/cdn/token \
  -H "X-Client-ID: $OBLIEN_CLIENT_ID" \
  -H "X-Client-Secret: $OBLIEN_CLIENT_SECRET"

Generate Admin Token

Admin tokens have full permissions including read, list, info, and delete.

const tokenData = await cdn.generateAdminToken();

// Returns:
// {
//   success: true,
//   token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
//   scope: "admin",
//   permissions: ["upload", "process", "read", "list", "info", "delete"],
//   expiresIn: "1m"
// }
const response = await fetch('https://api.oblien.com/cdn/token/admin', {
  method: 'POST',
  headers: {
    'X-Client-ID': process.env.OBLIEN_CLIENT_ID,
    'X-Client-Secret': process.env.OBLIEN_CLIENT_SECRET
  }
});

const tokenData = await response.json();
curl -X POST https://api.oblien.com/cdn/token/admin \
  -H "X-Client-ID: $OBLIEN_CLIENT_ID" \
  -H "X-Client-Secret: $OBLIEN_CLIENT_SECRET"

File Upload

Single File Upload

Upload a single file to the CDN.

// Get token from your backend first
const { token } = await fetch('/api/cdn-token').then(r => r.json());

// Upload file
const formData = new FormData();
formData.append('file', fileInput.files[0]);

const response = await fetch('https://cdn.oblien.com/api/', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${token}`
  },
  body: formData
});

const result = await response.json();
console.log(result.url); // https://cdn.oblien.com/abc123/image.jpg
// Server-side: Upload directly (SDK handles tokens automatically)
import fs from 'fs';

const fileBuffer = fs.readFileSync('image.jpg');

const result = await cdn.upload(fileBuffer, {
  filename: 'image.jpg'
});

console.log(result.url);
# Get token from your backend API first
TOKEN=$(curl -X POST http://your-backend.com/api/cdn-token | jq -r '.token')

# Upload file to CDN
curl -X POST https://cdn.oblien.com/api/ \
  -H "Authorization: Bearer $TOKEN" \
  -F "file=@image.jpg"

Multiple Files Upload

Upload multiple files at once.

const { token } = await fetch('/api/cdn-token').then(r => r.json());

const formData = new FormData();
Array.from(fileInput.files).forEach(file => {
  formData.append('files', file);
});

const response = await fetch('https://cdn.oblien.com/api/multiple', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${token}`
  },
  body: formData
});

const result = await response.json();
console.log(result.files); // Array of uploaded files
const files = [
  fs.readFileSync('image1.jpg'),
  fs.readFileSync('image2.jpg')
];

const result = await cdn.uploadMultiple(files, {
  filenames: ['image1.jpg', 'image2.jpg']
});

console.log(result.files);
TOKEN=$(curl -X POST http://your-backend.com/api/cdn-token | jq -r '.token')

curl -X POST https://cdn.oblien.com/api/multiple \
  -H "Authorization: Bearer $TOKEN" \
  -F "files=@image1.jpg" \
  -F "files=@image2.jpg"

Upload from URLs

Have the CDN download and process images from external URLs.

const { token } = await fetch('/api/cdn-token').then(r => r.json());

const response = await fetch('https://cdn.oblien.com/api/process-urls', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    urls: [
      'https://example.com/image1.jpg',
      'https://example.com/image2.png'
    ]
  })
});

const result = await response.json();
console.log(result.files);
const result = await cdn.uploadFromUrls([
  'https://example.com/image1.jpg',
  'https://example.com/image2.png'
]);

console.log(result.files);
TOKEN=$(curl -X POST http://your-backend.com/api/cdn-token | jq -r '.token')

curl -X POST https://cdn.oblien.com/api/process-urls \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "urls": [
      "https://example.com/image1.jpg",
      "https://example.com/image2.png"
    ]
  }'

File Management

Manage and track user's uploaded files using session authentication.

List Files

Get a paginated list of uploaded files.

const result = await cdn.listFiles({
  page: 1,
  limit: 50,
  sortBy: 'created_at',
  sortOrder: 'DESC'
});

console.log(result.data.files);
console.log(result.data.pagination);
const response = await fetch('https://api.oblien.com/cdn/files?page=1&limit=50', {
  headers: {
    'Cookie': 'session=your-session-cookie'
  }
});

const result = await response.json();
curl https://api.oblien.com/cdn/files?page=1&limit=50 \
  --cookie "session=your-session-cookie"

Response:

{
  "success": true,
  "data": {
    "files": [
      {
        "id": 123,
        "filename": "1234567890-abc123-image.jpg",
        "original_filename": "image.jpg",
        "cdn_url": "https://cdn.oblien.com/abc123/image.jpg",
        "size": 102400,
        "mime_type": "image/jpeg",
        "variants": [...],
        "upload_type": "upload",
        "is_deleted": false,
        "created_at": "2024-01-01T00:00:00.000Z"
      }
    ],
    "pagination": {
      "page": 1,
      "limit": 50,
      "total": 150,
      "totalPages": 3,
      "hasMore": true
    }
  }
}

Get Storage Stats

Get user's storage usage statistics.

const stats = await cdn.getStats();

console.log(stats.data);
// {
//   totalFiles: 150,
//   totalSize: 157286400,
//   activeFiles: 145,
//   activeSize: 152166400,
//   uploadedFiles: 100,
//   urlFiles: 50
// }
const response = await fetch('https://api.oblien.com/cdn/stats', {
  headers: {
    'Cookie': 'session=your-session-cookie'
  }
});

const stats = await response.json();
curl https://api.oblien.com/cdn/stats \
  --cookie "session=your-session-cookie"

Get File by ID

Get details of a specific file.

const file = await cdn.getFile(123);

console.log(file.data);
const response = await fetch('https://api.oblien.com/cdn/files/123', {
  headers: {
    'Cookie': 'session=your-session-cookie'
  }
});

const file = await response.json();
curl https://api.oblien.com/cdn/files/123 \
  --cookie "session=your-session-cookie"

Delete File

Soft delete a file (mark as deleted, can be restored).

const result = await cdn.deleteFile(123);

console.log(result.message); // "File marked as deleted"
const response = await fetch('https://api.oblien.com/cdn/files/123', {
  method: 'DELETE',
  headers: {
    'Cookie': 'session=your-session-cookie'
  }
});

const result = await response.json();
curl -X DELETE https://api.oblien.com/cdn/files/123 \
  --cookie "session=your-session-cookie"

Restore File

Restore a soft-deleted file.

const result = await cdn.restoreFile(123);

console.log(result.message); // "File restored successfully"
const response = await fetch('https://api.oblien.com/cdn/files/123/restore', {
  method: 'POST',
  headers: {
    'Cookie': 'session=your-session-cookie'
  }
});

const result = await response.json();
curl -X POST https://api.oblien.com/cdn/files/123/restore \
  --cookie "session=your-session-cookie"

Image Variants

Oblien CDN automatically generates optimized variants of uploaded images:

  • thumb - 150x150px thumbnail
  • medium - 800x600px medium size
  • blur - 75x75px blurred placeholder
  • full - Original full-size image

All variants are available immediately after upload.

Complete Example

Here's a complete example showing backend token generation and frontend upload:

Backend (Node.js/Express)

// backend/routes/cdn.js
import { OblienClient, OblienCDN } from 'oblien';
import express from 'express';

const router = express.Router();

const client = new OblienClient({
  clientId: process.env.OBLIEN_CLIENT_ID,
  clientSecret: process.env.OBLIEN_CLIENT_SECRET
});

const cdn = new OblienCDN(client);

// Generate CDN token
router.post('/cdn-token', async (req, res) => {
  try {
    const tokenData = await cdn.generateUserToken();
    res.json({ token: tokenData.token });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// List user's files
router.get('/my-files', async (req, res) => {
  try {
    const files = await cdn.listFiles({
      page: parseInt(req.query.page) || 1,
      limit: 50
    });
    res.json(files);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

export default router;

Frontend (React)

// frontend/components/FileUploader.jsx
import { useState } from 'react';

function FileUploader() {
  const [uploading, setUploading] = useState(false);
  const [uploadedFile, setUploadedFile] = useState(null);
  const [error, setError] = useState(null);

  const handleUpload = async (e) => {
    const file = e.target.files[0];
    if (!file) return;

    setUploading(true);
    setError(null);

    try {
      // Step 1: Get token from your backend
      const { token } = await fetch('/api/cdn-token', {
        method: 'POST'
      }).then(r => r.json());

      // Step 2: Upload to CDN
      const formData = new FormData();
      formData.append('file', file);

      const response = await fetch('https://cdn.oblien.com/api/', {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${token}`
        },
        body: formData
      });

      if (!response.ok) {
        throw new Error('Upload failed');
      }

      const result = await response.json();
      setUploadedFile(result);
    } catch (err) {
      setError(err.message);
    } finally {
      setUploading(false);
    }
  };

  return (
    <div>
      <input
        type="file"
        onChange={handleUpload}
        disabled={uploading}
        accept="image/*"
      />

      {uploading && <p>Uploading...</p>}
      {error && <p style={{ color: 'red' }}>{error}</p>}

      {uploadedFile && (
        <div>
          <h3>Uploaded Successfully!</h3>
          <img src={uploadedFile.url} alt="Uploaded" style={{ maxWidth: '400px' }} />
          <p>URL: {uploadedFile.url}</p>
          <p>Size: {(uploadedFile.size / 1024).toFixed(2)} KB</p>

          <h4>Variants:</h4>
          {uploadedFile.variants.map(v => (
            <div key={v.variant}>
              <strong>{v.variant}:</strong> {v.url}
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

export default FileUploader;

API Reference

CDN Endpoints

Token Generation (Backend)

  • POST /cdn/token - Generate user token
  • POST /cdn/token/admin - Generate admin token

File Upload (Frontend)

  • POST https://cdn.oblien.com/api/ - Upload single file
  • POST https://cdn.oblien.com/api/multiple - Upload multiple files
  • POST https://cdn.oblien.com/api/process-urls - Upload from URLs

File Management (Backend)

  • GET /cdn/files - List files
  • GET /cdn/stats - Get storage stats
  • GET /cdn/files/:id - Get file by ID
  • DELETE /cdn/files/:id - Delete file
  • POST /cdn/files/:id/restore - Restore file

Token Scopes

  • user - upload, process permissions
  • admin - All permissions including read, list, info, delete

Security

Critical:

  • Never expose Client ID/Secret in frontend code
  • Always generate tokens on your backend
  • Tokens expire in 1 minute for security
  • File management requires session authentication

Best Practices

  1. Token Generation - Always generate tokens on your backend, never in frontend code
  2. Token Expiration - Tokens expire in 1 minute. Generate fresh tokens for each upload
  3. Error Handling - Handle token expiration and network errors gracefully
  4. File Size - Check file size limits before upload to provide better UX
  5. Progress Tracking - Show upload progress for better user experience
  6. Image Optimization - Use appropriate variant sizes for different use cases

Troubleshooting

Token Expired

Tokens expire in 1 minute. Generate a fresh token before each upload:

// ❌ Bad: Reusing old token
const { token } = await getTokenOnce();
await upload1(token); // Works
await upload2(token); // May fail if > 1 minute passed

// ✅ Good: Fresh token for each upload
await upload1(await getToken());
await upload2(await getToken());

CORS Errors

The CDN endpoint https://cdn.oblien.com/api has CORS enabled. If you see CORS errors, check that you're using the correct endpoint and including the Authorization header.

File Size Limits

Different file types have different size limits. Use cdn.getLimits() to check current limits.

Next Steps