Complete Guide to JavaScript APIs: Fetch & Axios Tutorial

Master JavaScript API calls with this comprehensive guide covering Fetch API and Axios library. Learn HTTP methods, error handling, and best practices.

The Beginner's Guide to APIs with JavaScript (Fetch & Axios)

In today's interconnected web, APIs (Application Programming Interfaces) are the backbone of modern web development. Whether you're building a weather app, fetching user data, or integrating third-party services, understanding how to work with APIs in JavaScript is essential. This comprehensive guide will walk you through everything you need to know about making API calls using both the native Fetch API and the popular Axios library.

What is an API?

An API (Application Programming Interface) is a set of rules and protocols that allows different software applications to communicate with each other. Think of it as a waiter in a restaurant – you (the client) make a request through the waiter (API) to the kitchen (server), and the waiter brings back your food (data).

Types of APIs

REST APIs are the most common type you'll encounter. They use standard HTTP methods: - GET: Retrieve data - POST: Create new data - PUT: Update existing data - DELETE: Remove data

GraphQL APIs allow you to request specific data fields, reducing over-fetching.

WebSocket APIs enable real-time, bidirectional communication.

Understanding HTTP Requests and Responses

Before diving into code, let's understand the anatomy of HTTP requests and responses.

HTTP Request Components

`javascript // Example of what happens behind the scenes const request = { method: 'GET', url: 'https://api.example.com/users', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer your-token-here' }, body: JSON.stringify({ name: 'John' }) // Only for POST/PUT requests }; `

HTTP Response Components

`javascript // What you receive back const response = { status: 200, statusText: 'OK', headers: { 'content-type': 'application/json' }, data: { id: 1, name: 'John Doe', email: 'john@example.com' } }; `

Common HTTP Status Codes

- 200: Success - 201: Created - 400: Bad Request - 401: Unauthorized - 404: Not Found - 500: Internal Server Error

The Fetch API: JavaScript's Built-in Solution

The Fetch API is built into modern browsers and provides a clean, promise-based interface for making HTTP requests.

Basic Fetch Syntax

`javascript fetch(url, options) .then(response => response.json()) .then(data => console.log(data)) .catch(error => console.error('Error:', error)); `

Making Your First GET Request

`javascript // Simple GET request async function fetchUsers() { try { const response = await fetch('https://jsonplaceholder.typicode.com/users'); // Check if request was successful if (!response.ok) { throw new Error(HTTP error! status: ${response.status}); } const users = await response.json(); console.log(users); return users; } catch (error) { console.error('Failed to fetch users:', error); } }

fetchUsers(); `

POST Requests with Fetch

`javascript async function createUser(userData) { try { const response = await fetch('https://jsonplaceholder.typicode.com/users', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(userData) });

if (!response.ok) { throw new Error(HTTP error! status: ${response.status}); }

const newUser = await response.json(); console.log('User created:', newUser); return newUser; } catch (error) { console.error('Failed to create user:', error); } }

// Usage createUser({ name: 'Jane Doe', email: 'jane@example.com', phone: '123-456-7890' }); `

PUT and DELETE Requests

`javascript // Update user async function updateUser(userId, userData) { try { const response = await fetch(https://jsonplaceholder.typicode.com/users/${userId}, { method: 'PUT', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(userData) });

if (!response.ok) { throw new Error(HTTP error! status: ${response.status}); }

const updatedUser = await response.json(); return updatedUser; } catch (error) { console.error('Failed to update user:', error); } }

// Delete user async function deleteUser(userId) { try { const response = await fetch(https://jsonplaceholder.typicode.com/users/${userId}, { method: 'DELETE' });

if (!response.ok) { throw new Error(HTTP error! status: ${response.status}); }

console.log(User ${userId} deleted successfully); } catch (error) { console.error('Failed to delete user:', error); } } `

Introducing Axios: A Powerful HTTP Client

While Fetch is great, Axios offers additional features and a more convenient API. It's a popular third-party library that simplifies HTTP requests.

Installing Axios

`bash

Using npm

npm install axios

Using yarn

yarn add axios

Using CDN (in HTML)

`

Basic Axios Usage

`javascript import axios from 'axios';

// GET request async function fetchUsers() { try { const response = await axios.get('https://jsonplaceholder.typicode.com/users'); console.log(response.data); return response.data; } catch (error) { console.error('Error fetching users:', error.response?.data || error.message); } } `

Axios vs Fetch Comparison

`javascript // Fetch const fetchData = async () => { const response = await fetch('https://api.example.com/data'); const data = await response.json(); return data; };

// Axios - automatically parses JSON const axiosData = async () => { const response = await axios.get('https://api.example.com/data'); return response.data; }; `

Axios Configuration and Interceptors

`javascript // Create an axios instance with default config const api = axios.create({ baseURL: 'https://jsonplaceholder.typicode.com', timeout: 10000, headers: { 'Content-Type': 'application/json', } });

// Request interceptor api.interceptors.request.use( config => { // Add auth token to requests const token = localStorage.getItem('authToken'); if (token) { config.headers.Authorization = Bearer ${token}; } console.log('Request sent:', config); return config; }, error => { return Promise.reject(error); } );

// Response interceptor api.interceptors.response.use( response => { console.log('Response received:', response); return response; }, error => { if (error.response?.status === 401) { // Handle unauthorized access localStorage.removeItem('authToken'); window.location.href = '/login'; } return Promise.reject(error); } ); `

Error Handling Best Practices

Proper error handling is crucial for building robust applications.

Comprehensive Error Handling with Fetch

`javascript async function robustFetch(url, options = {}) { try { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 10000);

const response = await fetch(url, { ...options, signal: controller.signal });

clearTimeout(timeoutId);

if (!response.ok) { const errorData = await response.text(); throw new Error(HTTP ${response.status}: ${errorData}); }

const contentType = response.headers.get('content-type'); if (contentType && contentType.includes('application/json')) { return await response.json(); } else { return await response.text(); } } catch (error) { if (error.name === 'AbortError') { throw new Error('Request timed out'); } throw error; } } `

Error Handling with Axios

`javascript async function handleAxiosRequest() { try { const response = await axios.get('/api/data', { timeout: 5000 }); return response.data; } catch (error) { if (error.response) { // Server responded with error status console.error('Response error:', error.response.status, error.response.data); } else if (error.request) { // Request was made but no response received console.error('Network error:', error.request); } else { // Something else happened console.error('Error:', error.message); } throw error; } } `

Working with Different Data Formats

Handling JSON Data

`javascript // Fetch JSON data async function fetchJSON(url) { try { const response = await fetch(url); const data = await response.json(); return data; } catch (error) { console.error('JSON parsing error:', error); } }

// Send JSON data async function sendJSON(url, data) { try { const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(data) }); return await response.json(); } catch (error) { console.error('Error sending JSON:', error); } } `

Working with Form Data

`javascript // Send form data async function sendFormData(url, formData) { try { const response = await fetch(url, { method: 'POST', body: formData // Don't set Content-Type for FormData }); return await response.json(); } catch (error) { console.error('Error sending form data:', error); } }

// Create FormData from form element const form = document.getElementById('myForm'); const formData = new FormData(form);

// Or create FormData manually const manualFormData = new FormData(); manualFormData.append('name', 'John Doe'); manualFormData.append('email', 'john@example.com'); `

File Upload Example

`javascript async function uploadFile(file) { const formData = new FormData(); formData.append('file', file); formData.append('description', 'My uploaded file');

try { const response = await axios.post('/api/upload', formData, { headers: { 'Content-Type': 'multipart/form-data', }, onUploadProgress: (progressEvent) => { const percentCompleted = Math.round( (progressEvent.loaded * 100) / progressEvent.total ); console.log(Upload progress: ${percentCompleted}%); } }); return response.data; } catch (error) { console.error('Upload failed:', error); } } `

Authentication and Headers

API Key Authentication

`javascript const API_KEY = 'your-api-key-here';

async function fetchWithApiKey(url) { try { const response = await fetch(url, { headers: { 'Authorization': Bearer ${API_KEY}, 'Content-Type': 'application/json' } }); return await response.json(); } catch (error) { console.error('API request failed:', error); } } `

JWT Token Authentication

`javascript class ApiClient { constructor(baseURL) { this.baseURL = baseURL; this.token = localStorage.getItem('authToken'); }

async request(endpoint, options = {}) { const url = ${this.baseURL}${endpoint}; const config = { headers: { 'Content-Type': 'application/json', ...options.headers }, ...options };

if (this.token) { config.headers.Authorization = Bearer ${this.token}; }

try { const response = await fetch(url, config); if (response.status === 401) { this.handleUnauthorized(); throw new Error('Unauthorized'); }

return await response.json(); } catch (error) { console.error('API request failed:', error); throw error; } }

handleUnauthorized() { localStorage.removeItem('authToken'); // Redirect to login page window.location.href = '/login'; }

setToken(token) { this.token = token; localStorage.setItem('authToken', token); } }

const api = new ApiClient('https://api.example.com'); `

Building Your First API-Powered App: Weather Dashboard

Let's build a practical weather dashboard that demonstrates real API usage.

HTML Structure

`html

Weather Dashboard

`

JavaScript Weather App

`javascript class WeatherApp { constructor() { this.API_KEY = 'your-openweather-api-key'; // Get from openweathermap.org this.BASE_URL = 'https://api.openweathermap.org/data/2.5'; this.displayElement = document.getElementById('weatherDisplay'); }

async getCurrentWeather(city) { try { this.showLoading(); const response = await fetch( ${this.BASE_URL}/weather?q=${city}&appid=${this.API_KEY}&units=metric );

if (!response.ok) { throw new Error(Weather data not found for ${city}); }

const data = await response.json(); this.displayCurrentWeather(data); // Get 5-day forecast await this.getForecast(city); } catch (error) { this.showError(error.message); } }

async getForecast(city) { try { const response = await fetch( ${this.BASE_URL}/forecast?q=${city}&appid=${this.API_KEY}&units=metric );

if (!response.ok) { throw new Error('Forecast data not available'); }

const data = await response.json(); this.displayForecast(data); } catch (error) { console.error('Forecast error:', error); } }

displayCurrentWeather(data) { const { name, main, weather, wind } = data; const currentWeather = weather[0];

const html = `

${name}

${currentWeather.description}

${Math.round(main.temp)}°C

${currentWeather.description}

Feels like: ${Math.round(main.feels_like)}°C

Humidity: ${main.humidity}%

Wind Speed: ${wind.speed} m/s

Pressure: ${main.pressure} hPa

`;

this.displayElement.innerHTML = html; }

displayForecast(data) { const forecastHTML = `

5-Day Forecast

${data.list.filter((item, index) => index % 8 === 0).map(item => `

${new Date(item.dt * 1000).toLocaleDateString()}

${item.weather[0].description}

${Math.round(item.main.temp)}°C

${item.weather[0].description}

`).join('')}
`;

this.displayElement.innerHTML += forecastHTML; }

showLoading() { this.displayElement.innerHTML = '

Loading weather data...
'; }

showError(message) { this.displayElement.innerHTML =

Error: ${message}
; } }

// Initialize the app const weatherApp = new WeatherApp();

// Search function async function searchWeather() { const city = document.getElementById('cityInput').value.trim(); if (city) { await weatherApp.getCurrentWeather(city); } }

// Allow Enter key to trigger search document.getElementById('cityInput').addEventListener('keypress', function(e) { if (e.key === 'Enter') { searchWeather(); } });

// Load default city on page load window.addEventListener('load', () => { weatherApp.getCurrentWeather('London'); }); `

Building a Todo App with API Integration

Let's create a more complex example with full CRUD operations.

Todo App with Local Storage API

`javascript class TodoApp { constructor() { this.todos = []; this.API_URL = 'https://jsonplaceholder.typicode.com/todos'; this.init(); }

async init() { await this.loadTodos(); this.render(); this.bindEvents(); }

async loadTodos() { try { // For demo, we'll load from JSONPlaceholder and store locally const response = await fetch(${this.API_URL}?_limit=10); const todos = await response.json(); // Store in localStorage localStorage.setItem('todos', JSON.stringify(todos)); this.todos = todos; } catch (error) { console.error('Failed to load todos:', error); // Fallback to localStorage const stored = localStorage.getItem('todos'); this.todos = stored ? JSON.parse(stored) : []; } }

async addTodo(title) { const newTodo = { id: Date.now(), // Simple ID generation title: title.trim(), completed: false, userId: 1 };

try { // Simulate API call const response = await fetch(this.API_URL, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(newTodo) });

if (response.ok) { this.todos.unshift(newTodo); this.saveTodos(); this.render(); } } catch (error) { console.error('Failed to add todo:', error); } }

async toggleTodo(id) { const todoIndex = this.todos.findIndex(todo => todo.id === id); if (todoIndex === -1) return;

const todo = this.todos[todoIndex]; todo.completed = !todo.completed;

try { // Simulate API update await fetch(${this.API_URL}/${id}, { method: 'PUT', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(todo) });

this.saveTodos(); this.render(); } catch (error) { console.error('Failed to update todo:', error); // Revert on error todo.completed = !todo.completed; } }

async deleteTodo(id) { try { await fetch(${this.API_URL}/${id}, { method: 'DELETE' });

this.todos = this.todos.filter(todo => todo.id !== id); this.saveTodos(); this.render(); } catch (error) { console.error('Failed to delete todo:', error); } }

saveTodos() { localStorage.setItem('todos', JSON.stringify(this.todos)); }

render() { const container = document.getElementById('todoContainer'); const completedCount = this.todos.filter(todo => todo.completed).length; container.innerHTML = `

My Todos

${completedCount} of ${this.todos.length} completed

${this.todos.map(todo => `
${todo.title}
`).join('')}
`; }

handleAddTodo() { const input = document.getElementById('newTodoInput'); const title = input.value.trim(); if (title) { this.addTodo(title); input.value = ''; } }

bindEvents() { document.addEventListener('keypress', (e) => { if (e.key === 'Enter' && e.target.id === 'newTodoInput') { this.handleAddTodo(); } }); } }

// Initialize the todo app const todoApp = new TodoApp(); `

Advanced Techniques and Best Practices

Caching API Responses

`javascript class CachedApiClient { constructor(baseURL, cacheDuration = 5 60 1000) { // 5 minutes default this.baseURL = baseURL; this.cache = new Map(); this.cacheDuration = cacheDuration; }

async get(endpoint) { const cacheKey = ${this.baseURL}${endpoint}; const cached = this.cache.get(cacheKey);

// Return cached data if still valid if (cached && Date.now() - cached.timestamp < this.cacheDuration) { console.log('Returning cached data'); return cached.data; }

try { const response = await fetch(${this.baseURL}${endpoint}); const data = await response.json();

// Cache the response this.cache.set(cacheKey, { data, timestamp: Date.now() });

return data; } catch (error) { // Return cached data if available, even if expired if (cached) { console.log('Returning stale cached data due to error'); return cached.data; } throw error; } }

clearCache() { this.cache.clear(); } } `

Rate Limiting and Throttling

`javascript class RateLimitedApiClient { constructor(requestsPerSecond = 5) { this.requestsPerSecond = requestsPerSecond; this.requestQueue = []; this.isProcessing = false; }

async request(url, options) { return new Promise((resolve, reject) => { this.requestQueue.push({ url, options, resolve, reject }); this.processQueue(); }); }

async processQueue() { if (this.isProcessing || this.requestQueue.length === 0) { return; }

this.isProcessing = true;

while (this.requestQueue.length > 0) { const { url, options, resolve, reject } = this.requestQueue.shift();

try { const response = await fetch(url, options); const data = await response.json(); resolve(data); } catch (error) { reject(error); }

// Wait before processing next request await new Promise(resolve => setTimeout(resolve, 1000 / this.requestsPerSecond) ); }

this.isProcessing = false; } } `

Retry Logic for Failed Requests

`javascript async function fetchWithRetry(url, options = {}, maxRetries = 3) { for (let attempt = 1; attempt <= maxRetries; attempt++) { try { const response = await fetch(url, options); if (response.ok) { return await response.json(); } // Don't retry for client errors (4xx) if (response.status >= 400 && response.status < 500) { throw new Error(Client error: ${response.status}); } throw new Error(Server error: ${response.status}); } catch (error) { console.log(Attempt ${attempt} failed:, error.message); if (attempt === maxRetries) { throw error; } // Exponential backoff const delay = Math.pow(2, attempt) * 1000; console.log(Retrying in ${delay}ms...); await new Promise(resolve => setTimeout(resolve, delay)); } } } `

Testing API Calls

Simple Testing Framework

`javascript class ApiTester { constructor() { this.tests = []; }

async test(name, testFunction) { try { console.log(Running test: ${name}); await testFunction(); console.log(✅ ${name} passed); } catch (error) { console.error(❌ ${name} failed:, error.message); } }

async runAll() { for (const test of this.tests) { await this.test(test.name, test.fn); } } }

// Example tests const tester = new ApiTester();

tester.test('Fetch users returns array', async () => { const users = await fetch('https://jsonplaceholder.typicode.com/users') .then(r => r.json()); if (!Array.isArray(users)) { throw new Error('Response is not an array'); } if (users.length === 0) { throw new Error('Users array is empty'); } });

tester.test('Create user returns new user with ID', async () => { const newUser = await fetch('https://jsonplaceholder.typicode.com/users', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: 'Test User', email: 'test@example.com' }) }).then(r => r.json()); if (!newUser.id) { throw new Error('New user does not have an ID'); } });

// Run tests tester.runAll(); `

Performance Optimization

Parallel Requests

`javascript // Instead of sequential requests async function fetchSequential() { const users = await fetch('/api/users').then(r => r.json()); const posts = await fetch('/api/posts').then(r => r.json()); const comments = await fetch('/api/comments').then(r => r.json()); return { users, posts, comments }; }

// Use parallel requests async function fetchParallel() { const [users, posts, comments] = await Promise.all([ fetch('/api/users').then(r => r.json()), fetch('/api/posts').then(r => r.json()), fetch('/api/comments').then(r => r.json()) ]); return { users, posts, comments }; } `

Request Cancellation

`javascript class CancellableRequest { constructor() { this.controller = null; }

async fetch(url, options = {}) { // Cancel previous request if exists if (this.controller) { this.controller.abort(); }

this.controller = new AbortController(); try { const response = await fetch(url, { ...options, signal: this.controller.signal }); return await response.json(); } catch (error) { if (error.name === 'AbortError') { console.log('Request was cancelled'); } else { throw error; } } }

cancel() { if (this.controller) { this.controller.abort(); } } }

// Usage const request = new CancellableRequest();

// This will cancel the previous request if still pending request.fetch('/api/search?q=javascript'); request.fetch('/api/search?q=python'); // Cancels the above request `

Conclusion

Working with APIs in JavaScript is a fundamental skill for modern web development. Whether you choose the native Fetch API or the feature-rich Axios library, understanding how to make requests, handle responses, and manage errors will enable you to build powerful, data-driven applications.

Key takeaways from this guide:

1. Start with Fetch: It's built into browsers and perfect for basic API interactions 2. Consider Axios: For more complex applications requiring advanced features 3. Handle errors gracefully: Always implement proper error handling 4. Optimize performance: Use caching, parallel requests, and request cancellation 5. Test your API calls: Ensure reliability with proper testing 6. Secure your requests: Implement proper authentication and handle sensitive data carefully

Remember that working with APIs is as much about understanding the data and the service you're consuming as it is about the technical implementation. Always read the API documentation, understand rate limits, and respect the service's terms of use.

As you continue your journey with JavaScript and APIs, practice with different public APIs, experiment with various authentication methods, and gradually build more complex applications. The skills you've learned in this guide will serve as a solid foundation for creating amazing web experiences.

Tags

  • API
  • Axios
  • Fetch
  • HTTP
  • REST

Related Articles

Related Books - Expand Your Knowledge

Explore these JavaScript books to deepen your understanding:

Browse all IT books

Popular Technical Articles & Tutorials

Explore our comprehensive collection of technical articles, programming tutorials, and IT guides written by industry experts:

Browse all 8+ technical articles | Read our IT blog

Complete Guide to JavaScript APIs: Fetch &amp; Axios Tutorial