API Integration
How to communicate with the Yew Search backend API from the frontend.
Backend URL Configuration
The backend URL is configured in src/lib/config.ts:
import { config } from '$lib/config';
// Use in API calls
const url = `${config.BACKEND_URL}/v1/search`;
Default: http://localhost:8443
Authentication
All API calls use cookie-based authentication. Always include credentials: 'include':
const response = await fetch(`${config.BACKEND_URL}/v1/user`, {
method: 'GET',
credentials: 'include', // Required for cookies
headers: { 'Content-Type': 'application/json' },
});
Common Patterns
Simple GET Request
From search.svelte:
const response = await fetch(
`${config.BACKEND_URL}/v1/search?q=${encodeURIComponent(searchQuery)}`,
{
method: 'GET',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
}
);
if (!response.ok) {
throw new Error('Failed to fetch search results');
}
const data = await response.json();
Paginated GET Request
From user-integration.svelte:
const skip = (currentPage - 1) * itemsPerPage;
const response = await fetch(
`${config.BACKEND_URL}/v1/user-integration?skip=${skip}&limit=${itemsPerPage}`,
{
method: 'GET',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
}
);
const data = await response.json();
const items = data.results;
const totalPages = Math.ceil(data.pagination.total / itemsPerPage);
Multiple Parallel API Calls
From user-integration-by-id.svelte:
const [userIntegrationResponse, integrationResponse, contentResponse] = await Promise.all([
fetch(`${config.BACKEND_URL}/v1/user-integration/${id}`, {
method: 'GET',
credentials: 'include',
}),
fetch(`${config.BACKEND_URL}/v1/integration/${domain}`, {
method: 'GET',
credentials: 'include',
}),
fetch(`${config.BACKEND_URL}/v1/user-integration-content?...`, {
method: 'GET',
credentials: 'include',
}),
]);
DELETE Request with Confirmation
From user-integration.svelte:
const response = await fetch(
`${config.BACKEND_URL}/v1/user-integration/${integrationId}`,
{
method: 'DELETE',
credentials: 'include',
}
);
if (response.ok) {
toast.success('Success', {
description: 'Integration removed successfully',
});
// Update UI
} else {
toast.error('Error', {
description: 'Failed to remove integration. Please try again.',
});
}
Error Handling
403 Redirect to Login
if (response.status === 403) {
window.location.href = '/#/login';
return;
}
Try-Catch with Toast
try {
const response = await fetch(`${config.BACKEND_URL}/v1/...`);
if (!response.ok) {
throw new Error('API request failed');
}
const data = await response.json();
// Handle success
} catch (error) {
console.error('Failed to fetch data:', error);
toast.error('Error', {
description: 'Failed to load data. Please try again.',
});
}
TypeScript Interfaces
Define interfaces for API responses:
interface UserIntegration {
id: string;
userId: string;
integrationId: string;
integrationDomain: string;
config: Record<string, any>;
status: 'active' | 'inactive' | 'error';
createdAt: string;
updatedAt: string;
}
interface PaginatedResponse<T> {
results: T[];
pagination: {
skip: number;
limit: number;
total: number;
};
}
Query Parameter Construction
Simple
const query = encodeURIComponent(searchQuery);
const url = `${config.BACKEND_URL}/v1/search?q=${query}`;
Multiple Parameters
const apiUrl = new URL(`${config.BACKEND_URL}/v1/search`);
apiUrl.searchParams.set('q', searchQuery);
apiUrl.searchParams.set('skip', skip.toString());
apiUrl.searchParams.set('limit', limit.toString());
const response = await fetch(apiUrl.toString(), {
method: 'GET',
credentials: 'include',
});
Array Parameters
const domains = ['gmail.com', 'example.com'];
const domainsQuery = domains
.map((d) => `inDomains[]=${encodeURIComponent(d)}`)
.join('&');
const url = `${config.BACKEND_URL}/v1/integration?${domainsQuery}`;
Loading States
let isLoading = $state(false);
async function fetchData() {
isLoading = true;
try {
const response = await fetch(`${config.BACKEND_URL}/v1/...`);
const data = await response.json();
// Handle data
} finally {
isLoading = false; // Always reset loading state
}
}
Best Practices
- Always include
credentials: 'include'for authenticated requests - Use TypeScript interfaces for all API responses
- Handle errors gracefully with try-catch and user feedback
- Show loading states during API calls
- Encode query parameters with
encodeURIComponent() - Reset loading states in
finallyblocks - Use toast notifications for user feedback on success/error
- Redirect to login on 403 responses