Learn how to refresh asset sources when external changes occur outside CE.SDK in a server environment.
CE.SDK automatically refreshes the asset library for built-in operations like uploads and deletions. However, when assets are modified outside of CE.SDK—through a custom CMS, cloud storage, or third-party upload widget—the asset source won’t reflect these changes automatically. Use engine.asset.assetSourceContentsChanged() to notify the engine and trigger a refresh.
This guide covers when manual refresh is needed, how to trigger refreshes programmatically, and integration patterns for server-side asset management.
When to Use Asset Refresh#
CE.SDK handles asset refresh automatically for built-in operations. Manual refresh is required when external systems modify asset source content.
Automatic refresh (no action needed):
- Uploads using built-in sources like
ly.img.upload.* - Deletions through default upload handlers
- Modifications made through CE.SDK’s asset APIs
Manual refresh required:
- External uploads via third-party services (Cloudinary, S3)
- Backend modifications through CMS or API updates
- Sync with external storage (Azure Blob, Google Cloud Storage)
- Real-time collaboration when another process adds assets
Registering a Custom Asset Source#
Before refreshing assets, you need a custom asset source that fetches from your external system. The findAssets method queries your external data store each time assets are requested.
// Register a custom asset source that fetches from an external system// This source will need manual refresh when external changes occurengine.asset.addSource({ id: 'cloudinary-images', async findAssets(queryData): Promise<AssetsQueryResult> { // Fetch current assets from external data store const filteredAssets = externalAssets.filter( (asset) => !queryData.query || asset.name.toLowerCase().includes(queryData.query.toLowerCase()) );
return { assets: filteredAssets.map((asset) => ({ id: asset.id, label: asset.name, meta: { uri: asset.url, thumbUri: asset.url, blockType: '//ly.img.ubq/graphic' } })), total: filteredAssets.length, currentPage: queryData.page, nextPage: undefined }; }});
console.log('✓ Created custom asset source: cloudinary-images');This custom source fetches assets from an external data store (simulating Cloudinary, S3, or a CMS). When the external store changes, subsequent queries won’t reflect updates until you call assetSourceContentsChanged().
Refreshing After External Uploads#
When your backend receives new uploads from a third-party service, call assetSourceContentsChanged() to notify CE.SDK that the source contents have changed.
// Simulate an external upload (e.g., from Cloudinary upload widget)// In a real application, this would be triggered by webhook or pollingconst newAsset = { id: 'cloud-3', url: 'https://img.ly/static/ubq_samples/sample_3.jpg', name: 'Forest Path'};externalAssets.push(newAsset);
// Notify CE.SDK that the source contents have changedengine.asset.assetSourceContentsChanged('cloudinary-images');console.log('✓ External upload complete, asset source refreshed');The key is calling assetSourceContentsChanged('cloudinary-images') after the external upload completes. This tells CE.SDK to re-fetch assets on the next query.
Refreshing After External Modifications#
When your backend modifies asset metadata—renaming files, updating tags, or changing thumbnails—call assetSourceContentsChanged() to sync the asset source.
// Simulate backend modifications (e.g., CMS updates, API changes)externalAssets[0] = { ...externalAssets[0], name: 'Modified: Mountain Landscape'};
// Refresh the asset library to reflect changesengine.asset.assetSourceContentsChanged('cloudinary-images');console.log('✓ External modification complete, asset source refreshed');Any modification to assets in your external store requires a refresh. Without calling assetSourceContentsChanged(), subsequent queries may return stale data.
Refreshing After External Deletions#
When assets are deleted from your external system, call assetSourceContentsChanged() to ensure queries no longer return deleted assets.
// Simulate asset deletion from external systemconst removed = externalAssets.pop();console.log(`Removed asset from external store: ${removed?.name}`);
// Refresh the asset library to reflect the deletionengine.asset.assetSourceContentsChanged('cloudinary-images');console.log('✓ External deletion complete, asset source refreshed');The refresh ensures deleted assets no longer appear in query results. If you skip this step, the engine may reference assets that no longer exist.
Integration Patterns#
Webhook Handler#
Handle webhooks from external services to trigger refreshes:
app.post('/webhooks/cloudinary', (req, res) => { const { notification_type, public_id } = req.body;
if (notification_type === 'upload' || notification_type === 'delete') { engine.asset.assetSourceContentsChanged('cloudinary-images'); }
res.status(200).send('OK');});Scheduled Sync#
For periodic synchronization with external systems:
import cron from 'node-cron';
cron.schedule('*/5 * * * *', () => { // Refresh every 5 minutes engine.asset.assetSourceContentsChanged('external-assets');});Event-Driven Updates#
Listen for database changes to trigger refreshes:
database.on('assets:changed', (sourceId) => { engine.asset.assetSourceContentsChanged(sourceId);});Troubleshooting#
Assets not updating:
Verify the source ID passed to assetSourceContentsChanged() matches the ID used when registering the source with addSource(). Source IDs are case-sensitive.
Refresh not triggering:
Ensure you call assetSourceContentsChanged() after the external operation completes. If called before the upload finishes, findAssets() may return stale data.
Stale assets in queries:
Check that your findAssets implementation fetches fresh data on each call. Avoid caching responses unless you invalidate the cache when calling assetSourceContentsChanged().
Memory management:
Always dispose the engine when done processing to free resources. Use try/finally blocks to ensure cleanup happens even when errors occur.
API Reference#
| Method | Category | Purpose |
|---|---|---|
engine.asset.assetSourceContentsChanged(sourceID) | Asset API | Notify engine that asset source contents changed |
engine.asset.addSource(source) | Asset API | Register custom asset source |
engine.asset.findAssets(sourceID, query) | Asset API | Query assets from a source |