Based on my investigation, here's a comprehensive guide for integrating StreamSource as a backend for your client application.
StreamSource provides a RESTful API with JWT authentication for managing streaming sources. Currently, the API exposes endpoints for streams and user authentication, while streamers and timestamps are managed through the admin interface.
POST /api/v1/users/signup
Content-Type: application/json
{
"email": "your-app@example.com",
"password": "SecurePassword123!"
}POST /api/v1/users/login
Content-Type: application/json
{
"email": "your-app@example.com",
"password": "SecurePassword123!"
}
// Response:
{
"user": { "id": 1, "email": "...", "role": "default" },
"token": "eyJhbGciOiJIUzI1NiJ9..."
}- Tokens expire after 24 hours
- Include in all API requests:
Authorization: Bearer <token>
Create an abstraction layer to support multiple backends:
// Backend interface
class StreamBackend {
async createStream(data) { throw new Error('Not implemented'); }
async updateStream(id, data) { throw new Error('Not implemented'); }
async getStreams(filters) { throw new Error('Not implemented'); }
async deleteStream(id) { throw new Error('Not implemented'); }
}
// StreamSource implementation
class StreamSourceBackend extends StreamBackend {
constructor(apiUrl, token) {
super();
this.apiUrl = apiUrl;
this.token = token;
}
async request(endpoint, options = {}) {
const response = await fetch(`${this.apiUrl}${endpoint}`, {
...options,
headers: {
'Authorization': `Bearer ${this.token}`,
'Content-Type': 'application/json',
...options.headers
}
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || `HTTP ${response.status}`);
}
return response.json();
}
async createStream(data) {
return this.request('/api/v1/streams', {
method: 'POST',
body: JSON.stringify({
source: data.source,
link: data.link,
status: data.status || 'offline',
platform: data.platform,
title: data.title,
notes: data.notes
})
});
}
async getStreams(filters = {}) {
const params = new URLSearchParams(filters);
return this.request(`/api/v1/streams?${params}`);
}
}
// Legacy backend (if needed)
class LegacyBackend extends StreamBackend {
// Your existing implementation
}
// Backend manager
class BackendManager {
constructor(config) {
this.backends = {
streamSource: new StreamSourceBackend(config.streamSource.url, config.streamSource.token),
// legacy: new LegacyBackend(config.legacy) // if needed
};
this.primaryBackend = config.primaryBackend || 'streamSource';
}
async createStream(data) {
// Write to primary backend
const result = await this.backends[this.primaryBackend].createStream(data);
// Optionally sync to other backends
if (this.config.syncBackends) {
await this.syncToSecondaryBackends('create', data);
}
return result;
}
}class StreamManager {
constructor(backendManager, mapper) {
this.backend = backendManager;
this.mapper = mapper;
}
async createStream(data) {
// Validate required fields
if (!data.source || !data.link) {
throw new Error('Source and link are required');
}
// Create in StreamSource
const stream = await this.backend.createStream(data);
// Log for debugging
console.log('Created stream:', stream);
return stream;
}
async listStreams(filters = {}) {
// Supported filters: status, notStatus, is_pinned, page, per_page
const response = await this.backend.getStreams(filters);
return {
streams: response.streams,
pagination: response.meta
};
}
async updateStream(id, updates) {
return await this.backend.updateStream(id, updates);
}
async pinStream(id) {
return await this.backend.request(`/api/v1/streams/${id}/pin`, {
method: 'PUT'
});
}
}class RateLimitedBackend extends StreamSourceBackend {
constructor(apiUrl, token) {
super(apiUrl, token);
this.requestQueue = [];
this.rateLimitDelay = 100; // ms between requests
}
async request(endpoint, options) {
try {
return await super.request(endpoint, options);
} catch (error) {
if (error.message.includes('429')) {
// Rate limited - retry with exponential backoff
await this.delay(this.rateLimitDelay);
this.rateLimitDelay *= 2;
return this.request(endpoint, options);
}
throw error;
}
}
delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
}const config = {
backends: {
streamSource: {
url: process.env.STREAMSOURCE_API_URL || 'https://api.streamsource.com',
token: process.env.STREAMSOURCE_TOKEN,
enabled: true
},
// legacy: {
// enabled: false
// }
},
primaryBackend: 'streamSource',
syncBackends: true,
retryOptions: {
maxRetries: 3,
retryDelay: 1000
}
};Creates new user account.
- Request Body:
{ email: string, password: string } - Response:
{ user: object, token: string } - No authentication required
Authenticates existing user.
- Request Body:
{ email: string, password: string } - Response:
{ user: object, token: string } - No authentication required
All stream endpoints require JWT authentication via Authorization: Bearer <token> header.
Lists all streams for authenticated user.
- Query Parameters:
page: Page number (default: 1)per_page: Items per page (max: 100, default: 25)status: Filter by stream statusnotStatus: Exclude streams with specific statususer_id: Filter by specific useris_pinned: Filter by pinned statussort: Sort field (name,-name,created,-created)
- Response:
{ streams: array, meta: { current_page, total_pages, total_count, per_page } }
Creates new stream.
- Request Body:
- Required:
source,link - Optional:
status,platform,orientation,kind,city,state,notes,title,posted_by
- Required:
- Response: Stream object
Retrieves single stream details.
- Response: Stream object with relationships
Updates existing stream.
- Request Body: Any stream fields to update
- Response: Updated stream object
Deletes stream.
- Response: 204 No Content
Pins a stream.
- Response: Updated stream object
Unpins a stream.
- Response: Updated stream object
These require specific feature flags to be enabled:
- Feature Flag:
stream_analytics - Response: Analytics data
- Feature Flag:
stream_bulk_import - Request Body: Array of stream objects
- Response: Created streams
- Feature Flag:
stream_export - Query Parameters: Same as GET /api/v1/streams
- Response: Export data
- Only streams are currently available via API
- Streamers and timestamps require admin interface access
- Feature flags control access to bulk import/export
- Implement token refresh before 24-hour expiration
- Store tokens securely (never in code)
- 1000 requests/minute general limit
- 500 login attempts per 20 minutes
- 300 signup attempts per hour
- Implement retry logic with exponential backoff
- StreamSource auto-generates certain fields (timestamps)
- Platform detection is automatic based on URL
- Status transitions have business logic (30-minute continuation window)
- Monitor
/api-docsfor API updates - Check feature flags for new capabilities
- Plan for eventual streamer/timestamp API endpoints
{
"id": 123,
"source": "StreamerName",
"link": "https://streaming-platform.com/stream",
"status": "live",
"is_pinned": false,
"platform": "TikTok",
"orientation": "portrait",
"kind": "stream",
"city": "New York",
"state": "NY",
"notes": "Additional notes",
"title": "Stream Title",
"posted_by": "username",
"last_checked_at": "2025-01-05T10:00:00Z",
"last_live_at": "2025-01-05T09:30:00Z",
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-05T10:00:00Z",
"user": {
"id": 1,
"email": "user@example.com",
"role": "default"
}
}All API errors follow a consistent format:
{
"error": "Error message here"
}Common HTTP status codes:
401: Unauthorized (invalid/expired token)403: Forbidden (insufficient permissions)404: Resource not found422: Validation error429: Rate limit exceeded500: Server error
This guide provides a complete integration path to StreamSource, with a flexible architecture that supports multiple backends. The key is the abstraction layer that allows you to switch between or combine backends as needed.