Implementing Browser Extension REST API Integration with Server
A browser extension communicates with your server via REST API: synchronizes data, saves settings, and receives configuration updates. Extension-specific concerns: strict CSP policy, isolated execution context, and Manifest V3 restrictions.
Communication Architecture
Extension (content script)
↓ chrome.runtime.sendMessage
Extension (background service worker)
↓ fetch() → REST API
Server Backend
↓ JSON Response
Background service worker
↓ chrome.runtime.sendResponse
Content script
Background Service Worker (Manifest V3)
// background.js
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.action === 'save_item') {
saveItemToServer(message.data)
.then(result => sendResponse({ success: true, data: result }))
.catch(err => sendResponse({ success: false, error: err.message }));
return true; // async response
}
});
async function saveItemToServer(itemData) {
const token = await getStoredToken();
const resp = await fetch('https://api.example.com/v1/items', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token,
'X-Extension-Version': chrome.runtime.getManifest().version,
},
body: JSON.stringify(itemData),
});
if (!resp.ok) {
if (resp.status === 401) await refreshToken();
throw new Error(`API error: ${resp.status}`);
}
return resp.json();
}
CORS on Server
// Allow requests from extension
// Extension origin format: chrome-extension://EXTENSION_ID
class CorsMiddleware
{
public function handle(Request $request, Closure $next): Response
{
$origin = $request->header('Origin');
$allowedOrigins = [
'https://app.example.com',
'chrome-extension://' . config('services.extension.chrome_id'),
'moz-extension://' . config('services.extension.firefox_id'),
];
if (in_array($origin, $allowedOrigins)) {
return $next($request)->header('Access-Control-Allow-Origin', $origin);
}
return $next($request);
}
}
Settings Synchronization via chrome.storage.sync
// Synchronize settings with server and chrome.storage.sync
async function syncSettings() {
// Load from server
const serverSettings = await apiRequest('GET', '/v1/user/settings');
// Save locally
await chrome.storage.sync.set({ settings: serverSettings });
return serverSettings;
}
// When settings change — save to server
chrome.storage.sync.onChanged.addListener(async (changes) => {
if (changes.settings) {
await apiRequest('PUT', '/v1/user/settings', changes.settings.newValue);
}
});
Offline Mode Handling
const PENDING_ACTIONS_KEY = 'pending_actions';
async function executeOrQueue(action) {
try {
await fetch(action.url, action.options);
} catch (error) {
// No connection — queue the action
const pending = (await chrome.storage.local.get(PENDING_ACTIONS_KEY))[PENDING_ACTIONS_KEY] || [];
pending.push({ ...action, queued_at: Date.now() });
await chrome.storage.local.set({ [PENDING_ACTIONS_KEY]: pending });
}
}
// Execute pending actions when connection is restored
chrome.runtime.onStartup.addListener(flushPendingActions);
Timeline
REST API extension integration with synchronization and offline support: 4–6 business days.







