API Integration Techniques
Connecting Svelte to Laravel API
Integrating your Svelte frontend with a Laravel backend API is a common pattern for building modern web applications. This guide covers the best practices for API integration.
Setting Up Axios
Axios is a popular HTTP client that simplifies API requests.
// Install axios
npm install axios
// Create src/lib/api.js
import axios from 'axios';
// Create an axios instance with default config
const api = axios.create({
baseURL: import.meta.env.VITE_API_URL || 'http://localhost:8000/api',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
withCredentials: true // For cookie-based auth
});
// Request interceptor - add auth token
api.interceptors.request.use(config => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
// Response interceptor - handle common errors
api.interceptors.response.use(
response => response,
error => {
if (error.response?.status === 401) {
// Handle unauthorized (redirect to login, etc.)
localStorage.removeItem('token');
window.location.href = '/login';
}
return Promise.reject(error);
}
);
export default api;API Request Methods
Creating API Service Modules
It's a good practice to organize API calls into service modules by feature or entity.
// src/lib/services/posts.js
import api from '../api';
export const PostsService = {
// Get all posts
getAll: () => api.get('/posts'),
// Get a single post by ID
getById: (id) => api.get(`/posts/${id}`),
// Create a new post
create: (data) => api.post('/posts', data),
// Update a post
update: (id, data) => api.put(`/posts/${id}`, data),
// Delete a post
delete: (id) => api.delete(`/posts/${id}`)
};Using API Services in Components
<script>
import { onMount } from 'svelte';
import { PostsService } from '$lib/services/posts';
let posts = [];
let loading = true;
let error = null;
async function loadPosts() {
try {
loading = true;
const response = await PostsService.getAll();
posts = response.data;
} catch (err) {
error = err.response?.data?.message || 'Failed to load posts';
} finally {
loading = false;
}
}
onMount(loadPosts);
</script>
{#if loading}
<p>Loading...</p>
{:else if error}
<p class="error">{error}</p>
{:else}
<ul>
{#each posts as post}
<li>{post.title}</li>
{/each}
</ul>
{/if}Using SvelteKit's Data Loading
Load Function for API Data
SvelteKit provides a powerful data loading system with the load function.
// src/routes/posts/+page.js
import { PostsService } from '$lib/services/posts';
export async function load({ fetch }) {
try {
// Use the fetch instance provided by SvelteKit
const response = await PostsService.getAll();
return {
posts: response.data
};
} catch (error) {
return {
posts: [],
error: error.response?.data?.message || 'Failed to load posts'
};
}
}
// src/routes/posts/+page.svelte
<script>
// Access the loaded data
export let data;
// Destructure the data
const { posts, error } = data;
</script>
{#if error}
<p class="error">{error}</p>
{:else}
<ul>
{#each posts as post}
<li>{post.title}</li>
{/each}
</ul>
{/if}API Data Management with Svelte Stores
Creating Reactive Stores for API Data
Using Svelte stores to manage API data provides a reactive and centralized approach.
// src/stores/posts.js
import { writable } from 'svelte/store';
import { PostsService } from '$lib/services/posts';
// Create initial state
const initialState = {
posts: [],
loading: false,
error: null
};
// Create and export the store
function createPostsStore() {
const { subscribe, set, update } = writable(initialState);
return {
subscribe,
// Load all posts
fetchPosts: async () => {
update(state => ({ ...state, loading: true, error: null }));
try {
const response = await PostsService.getAll();
update(state => ({
...state,
posts: response.data,
loading: false
}));
} catch (error) {
update(state => ({
...state,
error: error.response?.data?.message || 'Failed to load posts',
loading: false
}));
}
},
// Add a new post
addPost: async (postData) => {
update(state => ({ ...state, loading: true, error: null }));
try {
const response = await PostsService.create(postData);
update(state => ({
...state,
posts: [...state.posts, response.data],
loading: false
}));
return response.data;
} catch (error) {
update(state => ({
...state,
error: error.response?.data?.message || 'Failed to create post',
loading: false
}));
throw error;
}
},
// Update an existing post
updatePost: async (id, postData) => {
update(state => ({ ...state, loading: true, error: null }));
try {
const response = await PostsService.update(id, postData);
update(state => ({
...state,
posts: state.posts.map(post =>
post.id === id ? response.data : post
),
loading: false
}));
return response.data;
} catch (error) {
update(state => ({
...state,
error: error.response?.data?.message || 'Failed to update post',
loading: false
}));
throw error;
}
},
// Delete a post
deletePost: async (id) => {
update(state => ({ ...state, loading: true, error: null }));
try {
await PostsService.delete(id);
update(state => ({
...state,
posts: state.posts.filter(post => post.id !== id),
loading: false
}));
} catch (error) {
update(state => ({
...state,
error: error.response?.data?.message || 'Failed to delete post',
loading: false
}));
throw error;
}
},
// Reset store to initial state
reset: () => set(initialState)
};
}
export const postsStore = createPostsStore();Using the Store in Components
<script>
import { onMount } from 'svelte';
import { postsStore } from '../stores/posts';
onMount(() => {
// Load posts when component mounts
postsStore.fetchPosts();
});
// Create a new post
async function handleCreatePost(formData) {
try {
await postsStore.addPost({
title: formData.title,
body: formData.body
});
// Handle success (e.g., show notification)
} catch (error) {
// Handle error
}
}
</script>
{#if $postsStore.loading}
<p>Loading...</p>
{:else if $postsStore.error}
<p class="error">{$postsStore.error}</p>
{:else}
<ul>
{#each $postsStore.posts as post}
<li>
{post.title}
<button on:click={() => postsStore.deletePost(post.id)}>Delete</button>
</li>
{/each}
</ul>
{/if}Error Handling Strategies
Handling API Errors
Properly handling API errors improves user experience and helps with debugging.
- Global Error Handling - Handle common errors like 401, 403, 500 in Axios interceptors
- Component-Level Error Handling - Use try/catch blocks for specific operations
- Error UI Components - Create reusable error message components
- Form Validation Errors - Handle Laravel validation errors (422 responses)
// Handling Laravel validation errors
async function submitForm(formData) {
try {
const response = await api.post('/endpoint', formData);
// Handle success
} catch (error) {
if (error.response?.status === 422) {
// Laravel validation errors
const validationErrors = error.response.data.errors;
// Map errors to form fields
Object.keys(validationErrors).forEach(field => {
formErrors[field] = validationErrors[field][0];
});
} else {
// Other errors
errorMessage = error.response?.data?.message || 'An error occurred';
}
}
}Demo: API Integration Example
Posts from API
No posts found.
Best Practices
API Integration Tips
- Use Environment Variables - Store API URLs in
.envfiles - Centralize API Logic - Create service modules by feature
- Handle Loading States - Always show loading indicators during requests
- Error Handling - Implement proper error handling at multiple levels
- Type Definitions - Use TypeScript interfaces for API responses
- Caching - Consider caching strategies for frequently accessed data
- Request Cancellation - Use AbortController to cancel pending requests
- Offline Support - Consider implementing offline capabilities