RAPI (REST API)
RAPI enables you to deploy auto-generated REST APIs based on your dcupl data models. Get OpenAPI-compliant endpoints with authentication, filtering, sorting, and pagination out of the box.
Overview
flowchart LR
subgraph Console
T[API Template]
D[Deploy]
end
subgraph Runner
I[RAPI Instance]
E[Endpoints]
end
subgraph Client["Client Apps"]
W[Web App]
M[Mobile App]
B[Backend]
end
T --> D
D --> I
I --> E
E --> W
E --> M
E --> B
RAPI provides:
- Auto-generated endpoints for all dcupl models
- OpenAPI/Swagger documentation for API exploration
- API key authentication with multiple keys support
- v-Guard caching for version-based cache invalidation
- Real-time monitoring of status and performance
Setting Up RAPI
Step 1: Create an API Template
- Navigate to REST API in your project
- Click Create RAPI
- Fill in the configuration:
{
"key": "customer-api",
"name": "Customer API",
"description": "REST API for customer data",
"vGuardEnabled": false,
"auth": [
{
"type": "api-key",
"value": "your-secure-api-key-here"
}
],
"dcuplConfig": {
"projectId": "your-project-id",
"apiKey": "your-sdk-readonly-key",
"processOptions": {
"applicationKey": "production",
"environmentKeys": ["prod"]
}
}
}Step 2: Deploy to Runner
- Select your API template
- Click Deploy
- Choose target runner instance
- Wait for status to show Ready
Step 3: Access Your API
Your API is now available at:
{runner-url}/instance/{api-key}/Authentication
API Key Authentication
RAPI supports API key authentication via query parameter or header:
curl 'https://runner.example.com/instance/customer-api/customer?api-key=your-api-key'curl 'https://runner.example.com/instance/customer-api/customer' \
-H 'api-key: your-api-key'Best Practice: Use header-based authentication in production as it's more secure and doesn't expose keys in URLs or logs.
Multiple API Keys
Configure multiple keys for different consumers:
{
"auth": [
{ "type": "api-key", "value": "key-for-web-app" },
{ "type": "api-key", "value": "key-for-mobile-app" },
{ "type": "api-key", "value": "key-for-partner-integration" }
]
}Security Best Practices
- Generate strong, random keys (32+ characters)
- Rotate keys periodically
- Use different keys per environment
- Store keys in environment variables, never in code
- Monitor key usage for anomalies
API Endpoints
Status Endpoint
Check if the RAPI instance is ready:
GET /instance/customer-api/status?api-key=your-key{
"status": "ready",
"version": "abc123",
"memory": {
"used": 50,
"max": 512
}
}Status values:
| Status | Description |
|---|---|
waiting |
Instance starting up |
processing |
Loading data |
ready |
Accepting requests |
updating |
Reloading configuration |
error |
Initialization failed |
Overview Endpoint
Get available models and endpoints:
GET /instance/customer-api/overview?api-key=your-key{
"key": "customer-api",
"name": "Customer API",
"models": ["customer", "order", "product"],
"endpoints": ["/customer", "/customer/:id", "/order", "/product"]
}Swagger Documentation
Access interactive API documentation:
GET /instance/customer-api/swagger?api-key=your-keyReturns OpenAPI 3.0 specification. Open in browser for interactive Swagger UI.
Model Endpoints
For each dcupl model, RAPI generates REST endpoints:
List Items
GET /instance/customer-api/customer?api-key=your-key[
{ "key": "c1", "name": "John Doe", "email": "john@example.com" },
{ "key": "c2", "name": "Jane Smith", "email": "jane@example.com" }
]Get Single Item
GET /instance/customer-api/customer/c1?api-key=your-key{
"key": "c1",
"name": "John Doe",
"email": "john@example.com",
"orders": [{ "key": "o1", "total": 150.0 }]
}Filtering
Filter results using query parameters:
# Simple equality
GET /customer?filter[status]=active
# Greater than
GET /customer?filter[age][$gt]=25
# Less than
GET /customer?filter[createdAt][$lt]=2025-01-01
# In array
GET /customer?filter[country][$in]=US,UK,DE
# Contains (string search)
GET /customer?filter[name][$contains]=johnSorting
# Sort ascending
GET /customer?sort=name
# Sort descending
GET /customer?sort=-createdAt
# Multi-field sort
GET /customer?sort=country,-namePagination
# Limit results
GET /customer?limit=10
# With offset
GET /customer?limit=10&offset=20
# Page-based
GET /customer?page=3&limit=10v-Guard Caching
Enable version-based cache invalidation for improved performance:
{
"vGuardEnabled": true
}How v-Guard Works
sequenceDiagram
participant Client
participant RAPI
participant Cache
Client->>RAPI: GET /customer?v=abc123
RAPI->>Cache: Check version
alt Version matches
Cache-->>Client: Return cached data
else Version mismatch
RAPI->>RAPI: Reload data
RAPI-->>Client: Return fresh data
end
Client Integration
class RAPIClient {
private version: string | null = null;
async fetch(endpoint: string) {
const params = new URLSearchParams();
if (this.version) {
params.set('v', this.version);
}
const response = await fetch(`${this.baseUrl}/${endpoint}?${params}`, {
headers: { 'api-key': this.apiKey },
});
// Update version from response
const newVersion = response.headers.get('x-dcupl-version');
if (newVersion) {
this.version = newVersion;
}
return response.json();
}
}When to enable v-Guard:
- Data changes infrequently
- Performance is critical
- Client can track version
When to disable:
- Real-time data required
- Per-request authentication needed
Client Integration Examples
JavaScript/TypeScript SDK
class RAPIClient {
constructor(
private baseUrl: string,
private instanceKey: string,
private apiKey: string
) {}
private async request<T>(path: string, options?: RequestInit): Promise<T> {
const response = await fetch(`${this.baseUrl}/instance/${this.instanceKey}/${path}`, {
...options,
headers: {
'api-key': this.apiKey,
'Content-Type': 'application/json',
...options?.headers,
},
});
if (!response.ok) {
throw new Error(`RAPI Error: ${response.status} ${response.statusText}`);
}
return response.json();
}
// List items with optional filtering
async list<T>(
model: string,
options?: {
filter?: Record<string, any>;
sort?: string;
limit?: number;
offset?: number;
}
): Promise<T[]> {
const params = new URLSearchParams();
if (options?.filter) {
Object.entries(options.filter).forEach(([key, value]) => {
params.append(`filter[${key}]`, String(value));
});
}
if (options?.sort) params.append('sort', options.sort);
if (options?.limit) params.append('limit', String(options.limit));
if (options?.offset) params.append('offset', String(options.offset));
const query = params.toString();
return this.request(`${model}${query ? `?${query}` : ''}`);
}
// Get single item
async get<T>(model: string, id: string): Promise<T> {
return this.request(`${model}/${id}`);
}
// Check status
async status(): Promise<{ status: string; version: string }> {
return this.request('status');
}
}
// Usage
const client = new RAPIClient(
'https://runner.example.com',
'customer-api',
process.env.RAPI_API_KEY!
);
const customers = await client.list('customer', {
filter: { status: 'active' },
sort: '-createdAt',
limit: 10,
});React Hook
import { useState, useEffect } from 'react';
function useRAPI<T>(model: string, options?: {
filter?: Record<string, any>;
sort?: string;
}) {
const [data, setData] = useState<T[] | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
const fetchData = async () => {
try {
setLoading(true);
const params = new URLSearchParams();
if (options?.filter) {
Object.entries(options.filter).forEach(([key, value]) => {
params.append(`filter[${key}]`, String(value));
});
}
if (options?.sort) params.append('sort', options.sort);
const response = await fetch(
`${process.env.REACT_APP_RAPI_URL}/${model}?${params}`,
{ headers: { 'api-key': process.env.REACT_APP_RAPI_KEY! } }
);
if (!response.ok) throw new Error('Failed to fetch');
setData(await response.json());
} catch (err) {
setError(err instanceof Error ? err : new Error('Unknown error'));
} finally {
setLoading(false);
}
};
fetchData();
}, [model, JSON.stringify(options)]);
return { data, loading, error };
}
// Usage in component
function CustomerList() {
const { data, loading, error } = useRAPI<Customer>('customer', {
filter: { status: 'active' },
sort: 'name'
});
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<ul>
{data?.map(customer => (
<li key={customer.key}>{customer.name}</li>
))}
</ul>
);
}Configuration Reference
Template Structure
interface RAPITemplate {
key: string; // Unique identifier (alphanumeric, -, _)
name: string; // Display name
description?: string; // Optional description
version?: string; // Optional version string
vGuardEnabled: boolean; // Enable version-based caching
auth: AuthConfig[]; // Authentication configuration
dcuplConfig: DcuplConfig; // dcupl integration config
}
interface AuthConfig {
type: 'api-key';
value: string;
}
interface DcuplConfig {
projectId: string;
apiKey: string; // SDK readonly key
processOptions: {
applicationKey?: string; // Select application preset
environmentKeys?: string[]; // Select environments
resourceTags?: string[][]; // Filter by resource tags
variables?: Record<string, any>;
};
loaderFetchOptions?: {
baseUrl?: string; // Custom CDN URL
loaderFileName?: string; // Custom loader filename
};
}Troubleshooting
Instance Status: Error
Causes:
- Invalid SDK API key
- Project not accessible
- Data loading failed
Solutions:
- Verify SDK API key in template
- Check project permissions
- Review runner logs
Endpoints Not Responding
Causes:
- Instance not deployed
- Runner offline
- Incorrect API key
Solutions:
- Check instance status
- Verify runner connectivity
- Test API key authentication
High Memory Usage
Causes:
- Large datasets
- Too many models loaded
Solutions:
- Filter resources by tags
- Use specific application presets
- Increase runner memory limits
What's Next?
- Workflows - Build data processing pipelines
- AI Features - Generate APIs with AI assistance
- SDK Integration - Use dcupl SDK alongside RAPI