DEV Community

Cover image for Mastering Axios: A Technical Guide to Building Your Food Delivery App🍔✨
Ankita Kanchan
Ankita Kanchan

Posted on

Mastering Axios: A Technical Guide to Building Your Food Delivery App🍔✨

In today’s world, efficient and reliable HTTP requests are the backbone of any web application, especially for something as critical as a food delivery app. In this guide, we'll explore how to use Axios to build and enhance a food delivery app by making seamless API requests, handling responses, managing errors, and much more.


1. Introduction to Axios

Axios is a promise-based HTTP client that is widely used for making HTTP requests in JavaScript applications. It works seamlessly in both the browser and Node.js environments. Here's why Axios is often chosen over other HTTP clients:

Why Choose Axios?

  • Promise-Based Interface: Axios leverages promises, making it easy to work with modern async/await syntax, leading to cleaner and more readable code.
  • Request and Response Interceptors: Provides hooks to run custom code or modify requests/responses globally.
  • Automatic JSON Data Handling: No need to manually parse or stringify JSON data—Axios does it for you.
  • Broad Browser Support: Axios is supported across all modern browsers and can be used in Node.js.

Installing Axios

To get started, you need to install Axios in your project:

npm install axios
Enter fullscreen mode Exit fullscreen mode

Or if you prefer using yarn:

yarn add axios
Enter fullscreen mode Exit fullscreen mode

After installation, you can import Axios in your JavaScript files:

import axios from 'axios';
Enter fullscreen mode Exit fullscreen mode

With Axios installed and ready, let's move on to the core functionalities.


2. Making HTTP Requests

In a food delivery app, interacting with various APIs is crucial. Whether you’re fetching a list of restaurants, placing an order, or tracking delivery, Axios makes this easy.

Fetching Restaurant Data (GET Request)

Let’s start by fetching a list of restaurants. Here’s how you can make a simple GET request:

axios.get('https://api.foodapp.com/restaurants')
  .then(response => {
    console.log('List of Restaurants:', response.data);
    // Further processing or updating the UI
  })
  .catch(error => {
    console.error('Error fetching restaurant data:', error);
  });
Enter fullscreen mode Exit fullscreen mode

In this example, Axios sends a GET request to the API endpoint to retrieve restaurant data. The then block handles the successful response, while the catch block captures and logs any errors that occur.

Placing an Order (POST Request)

When a user decides to place an order, you’ll need to send that order data to the server. Here’s how:

axios.post('https://api.foodapp.com/orders', {
  restaurantId: 42,
  items: [
    { id: 101, name: 'Margherita Pizza', quantity: 1 },
    { id: 202, name: 'Caesar Salad', quantity: 2 }
  ],
  userId: 301,
  deliveryAddress: '123 Main Street'
})
.then(response => {
  console.log('Order placed successfully:', response.data);
  // Redirect user to order confirmation page
})
.catch(error => {
  console.error('Error placing order:', error);
});
Enter fullscreen mode Exit fullscreen mode

Here, we’re sending a POST request with the order details in the request body. This information is typically sent as JSON. The server processes the order and returns a confirmation in the response, which you can then use to update the UI.

Updating Order Status (PUT Request)

If you need to update an existing order, such as changing the delivery address or order items, you can use a PUT request:

axios.put('https://api.foodapp.com/orders/123', {
  deliveryAddress: '456 New Street'
})
.then(response => {
  console.log('Order updated successfully:', response.data);
})
.catch(error => {
  console.error('Error updating order:', error);
});
Enter fullscreen mode Exit fullscreen mode

PUT requests are used to update resources on the server. In this case, the delivery address of an order is being updated.


3. Request Configuration: Customizing Your Requests

Axios allows you to configure your requests globally or per-request, giving you flexibility in how you interact with APIs.

Global Configuration

Instead of setting the same base URL or headers in every request, you can define them globally:

axios.defaults.baseURL = 'https://api.foodapp.com';
axios.defaults.headers.common['Authorization'] = 'Bearer YOUR_TOKEN_HERE';
axios.defaults.timeout = 5000;  // Set a global timeout of 5 seconds
Enter fullscreen mode Exit fullscreen mode

This configuration is applied to all requests, saving you from repetition and ensuring consistency across your app.

Per-Request Configuration

Sometimes, you might need to configure specific requests differently. For instance, you might want to override the global timeout for a particular request:

axios.get('/restaurants', {
  timeout: 10000  // Override global timeout for this request
})
.then(response => console.log('Restaurant data:', response.data))
.catch(error => console.error('Error fetching data:', error));
Enter fullscreen mode Exit fullscreen mode

This flexibility allows you to tailor each request to the specific needs of your app.


4. Handling Responses: Processing Data Efficiently

After making a request, the response from the server needs to be handled appropriately. Axios provides a clean way to access not just the data, but also additional response details.

Inspecting the Response Object

The response object from Axios includes more than just data; it provides the status, headers, and more:

axios.get('/restaurants')
  .then(response => {
    console.log('Status:', response.status);  // HTTP status code
    console.log('Headers:', response.headers);  // HTTP headers
    console.log('Data:', response.data);  // Response data (restaurants)
  })
  .catch(error => {
    console.error('Error fetching data:', error);
  });
Enter fullscreen mode Exit fullscreen mode

This information can be crucial for tasks such as handling different HTTP statuses or logging response times.

Transforming Response Data

Sometimes, the data you receive isn’t in the exact format you need. Axios allows you to transform this data easily:

axios.get('/restaurants')
  .then(response => {
    const formattedData = response.data.map(restaurant => ({
      name: restaurant.name.toUpperCase(),
      cuisine: restaurant.cuisine,
      rating: `${restaurant.rating} stars`
    }));
    console.log('Formatted Restaurants:', formattedData);
  })
  .catch(error => {
    console.error('Error fetching or transforming data:', error);
  });
Enter fullscreen mode Exit fullscreen mode

This transformation ensures that the data is in the most useful format for your application, reducing the need for additional processing later on.


5. Error Handling: Managing Failures Gracefully

Errors are inevitable, especially when dealing with network requests. Axios provides a robust mechanism for catching and handling these errors.

Basic Error Handling

Here’s how you can catch and handle errors in your Axios requests:

axios.get('/restaurants')
  .catch(error => {
    if (error.response) {
      console.error('Server responded with an error:', error.response.status);
    } else if (error.request) {
      console.error('No response received:', error.request);
    } else {
      console.error('Error setting up request:', error.message);
    }
  });
Enter fullscreen mode Exit fullscreen mode

This code snippet handles different types of errors:

  • Response errors occur when the server responds with a status outside the 2xx range.
  • Request errors happen when the request is made but no response is received (e.g., network issues).
  • Setup errors occur before the request is even made (e.g., a misconfigured request).

Retrying Requests

Sometimes, you may want to automatically retry a failed request, especially for transient errors like network hiccups:

function retryRequest(config, retries = 3) {
  return axios(config).catch(error => {
    if (retries > 0) {
      console.log(`Retrying request... (${retries} retries left)`);
      return retryRequest(config, retries - 1);
    } else {
      return Promise.reject(error);
    }
  });
}

retryRequest({ url: '/restaurants' })
  .then(response => console.log('Data:', response.data))
  .catch(error => console.error('Final error after retries:', error));
Enter fullscreen mode Exit fullscreen mode

This recursive function retries the request a specified number of times before finally throwing an error. It’s particularly useful for scenarios where temporary network issues are common.


6. Interceptors: Advanced Request and Response Handling

Interceptors allow you to run your code or modify requests and responses before they are handled by then or catch.

Request Interceptors

Let’s say you want to attach an authentication token to every request:

axios.interceptors.request.use(config => {
  config.headers['Authorization'] = 'Bearer YOUR_TOKEN_HERE';
  console.log('Request made with:', config);
  return config;
}, error => {
  return Promise.reject(error);
});
Enter fullscreen mode Exit fullscreen mode

This interceptor automatically adds the authorization header to every outgoing request, ensuring that the user is authenticated for all API calls.

Response Interceptors

You can also use interceptors to handle responses globally:

axios.interceptors.response.use(response => {
  console.log('Response received:', response);
  return response;
}, error => {
  if (error.response.status === 401) {
    console.error('Unauthorized access - redirecting to login.');
    // Redirect to login or refresh the token
  }
  return Promise.reject(error);
});
Enter fullscreen mode Exit fullscreen mode

This response interceptor checks for specific status codes, such as 401 Unauthorized, and allows you to take action,

such as redirecting to a login page.


7. Cancelling Requests: Handling User Navigation and Timeouts

In some cases, you might want to cancel an HTTP request, for example, if the user navigates away from the page before the request completes.

Cancelling an Axios Request

Here’s how you can cancel a request using Axios:

const CancelToken = axios.CancelToken;
const source = CancelToken.source();

axios.get('/restaurants', {
  cancelToken: source.token
})
.then(response => {
  console.log('Restaurants data:', response.data);
})
.catch(thrown => {
  if (axios.isCancel(thrown)) {
    console.log('Request canceled:', thrown.message);
  } else {
    console.error('Request failed:', thrown);
  }
});

// Later, cancel the request
source.cancel('Operation canceled by the user.');
Enter fullscreen mode Exit fullscreen mode

By using a cancel token, you can easily cancel the request if the user navigates away or if you determine that the request is no longer necessary.


8. Concurrent Requests: Efficiently Managing Multiple API Calls

In a food delivery app, you might need to fetch data from multiple endpoints simultaneously, such as restaurant details and current promotions.

Handling Multiple Requests

Axios makes it simple to perform concurrent requests using axios.all:

axios.all([
  axios.get('/restaurants/42'),
  axios.get('/promotions/current')
])
.then(axios.spread((restaurantResponse, promotionsResponse) => {
  console.log('Restaurant:', restaurantResponse.data);
  console.log('Promotions:', promotionsResponse.data);
}))
.catch(error => {
  console.error('Error with concurrent requests:', error);
});
Enter fullscreen mode Exit fullscreen mode

In this example, two requests are made simultaneously, and their results are handled together, improving the efficiency of your application by reducing the overall loading time.


9. Axios and Authentication: Securing Your API Interactions

Authentication is a critical aspect of any application that handles sensitive data, such as user profiles and payment information.

Handling Login Requests

Here’s how you can manage user login and store the authentication token:

axios.post('/auth/login', {
  username: 'john_doe',
  password: 'securepassword123'
})
.then(response => {
  const token = response.data.token;
  axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
  console.log('User logged in successfully.');
})
.catch(error => {
  console.error('Login failed:', error);
});
Enter fullscreen mode Exit fullscreen mode

After a successful login, the token is stored and attached to all subsequent requests, ensuring that the user remains authenticated throughout their session.

Refreshing Tokens

For long-running sessions, you might need to refresh the authentication token. Here’s a simple way to handle token refresh:

axios.interceptors.response.use(response => {
  return response;
}, error => {
  const originalRequest = error.config;
  if (error.response.status === 401 && !originalRequest._retry) {
    originalRequest._retry = true;
    return axios.post('/auth/refresh-token')
      .then(res => {
        if (res.status === 200) {
          axios.defaults.headers.common['Authorization'] = `Bearer ${res.data.token}`;
          originalRequest.headers['Authorization'] = `Bearer ${res.data.token}`;
          return axios(originalRequest);
        }
      });
  }
  return Promise.reject(error);
});
Enter fullscreen mode Exit fullscreen mode

This interceptor checks for a 401 Unauthorized response and attempts to refresh the token before retrying the original request.


10. Customizing Axios: Tailoring Axios to Fit Your Application

In complex applications, you might need to create multiple Axios instances with different configurations.

Creating a Custom Axios Instance

For example, you might want a separate instance for making API calls related to user accounts:

const userApi = axios.create({
  baseURL: 'https://api.foodapp.com/users',
  timeout: 3000,
  headers: {'X-Custom-Header': 'userAPI'}
});

userApi.get('/profile')
  .then(response => console.log('User profile:', response.data))
  .catch(error => console.error('Error fetching user profile:', error));
Enter fullscreen mode Exit fullscreen mode

This instance is configured with a specific base URL and timeout, making it easier to manage different types of requests in your app.


11. Using Axios with Async/Await: Simplifying Your Asynchronous Code

Async/await syntax is a powerful feature in JavaScript that simplifies working with asynchronous code, making it more readable and maintainable.

Fetching Data with Async/Await

Here’s how you can use async/await with Axios to fetch restaurant data:

async function fetchRestaurantData() {
  try {
    const response = await axios.get('/restaurants');
    console.log('Restaurant data:', response.data);
  } catch (error) {
    console.error('Error fetching restaurant data:', error);
  }
}

fetchRestaurantData();
Enter fullscreen mode Exit fullscreen mode

This approach allows you to write asynchronous code that looks synchronous, making it easier to understand and debug.


12. Working with Cookies: Maintaining Session State

Cookies are often used to maintain session state, particularly in web applications that require user authentication.

Sending Cookies with Requests

If your backend uses cookies to manage sessions, you can ensure they’re sent with every request:

axios.defaults.withCredentials = true;

axios.get('/user/profile')
  .then(response => console.log('User profile:', response.data))
  .catch(error => console.error('Error fetching profile:', error));
Enter fullscreen mode Exit fullscreen mode

By setting withCredentials to true, Axios will include cookies in cross-site requests, enabling session management.


13. Uploading and Downloading Files: Handling Media with Axios

In a food delivery app, you might need to upload images (e.g., restaurant logos) or download files (e.g., PDF menus).

Uploading an Image

Here’s how to upload an image file to the server:

const formData = new FormData();
formData.append('image', fileInput.files[0]);

axios.post('/restaurants/42/logo', formData, {
  headers: {
    'Content-Type': 'multipart/form-data'
  }
})
.then(response => console.log('Image uploaded successfully:', response.data))
.catch(error => console.error('Error uploading image:', error));
Enter fullscreen mode Exit fullscreen mode

This code uses FormData to send a file along with the request, ensuring it’s handled correctly by the server.

Downloading a File

And here’s how to download a file, such as a PDF menu:

axios({
  url: '/restaurants/42/menu.pdf',
  method: 'GET',
  responseType: 'blob', // Ensures the response is treated as binary data
})
.then(response => {
  const url = window.URL.createObjectURL(new Blob([response.data]));
  const link = document.createElement('a');
  link.href = url;
  link.setAttribute('download', 'menu.pdf');
  document.body.appendChild(link);
  link.click();
})
.catch(error => console.error('Error downloading file:', error));
Enter fullscreen mode Exit fullscreen mode

This method downloads the file and triggers a download in the user’s browser.


14. Axios Adapters: Extending Axios for Custom Use Cases

Axios adapters allow you to extend or modify how Axios handles requests, which can be useful in specialized scenarios.

Creating a Custom Adapter

Let’s say you want to log every request to a database for analytics purposes:

function loggingAdapter(config) {
  // Log request to database
  logRequestToDatabase(config);

  // Proceed with the default request handling
  return axios.defaults.adapter(config);
}

const customAxios = axios.create({
  adapter: loggingAdapter
});

customAxios.get('/restaurants')
  .then(response => console.log('Restaurant data:', response.data))
  .catch(error => console.error('Error fetching data:', error));
Enter fullscreen mode Exit fullscreen mode

This adapter logs each request before proceeding with the default handling, allowing you to extend Axios’s behavior without modifying its core functionality.


15. Debugging and Logging: Keeping Your App Running Smoothly

Effective logging and debugging are essential for maintaining a robust application, especially when dealing with complex API interactions.

Logging Requests and Responses

Here’s how you can log all outgoing requests and incoming responses:

axios.interceptors.request.use(request => {
  console.log('Starting Request', request);
  return request;
});

axios.interceptors.response.use(response => {
  console.log('Response:', response);
  return response;
});
Enter fullscreen mode Exit fullscreen mode

This setup logs the full details of every request and response, providing valuable insights during development and debugging.

Handling Errors in Debug Mode

For development, you might want to log additional details about errors:

axios.interceptors.response.use(
  response => response,
  error => {
    if (process.env.NODE_ENV === 'development') {
      console.error('Error response:', error.response);
    }
    return Promise.reject(error);
  }
);
Enter fullscreen mode Exit fullscreen mode

This interceptor logs error details only in development mode, helping you diagnose issues without cluttering your production logs.


Conclusion

Axios is a powerful tool that can significantly streamline your HTTP requests, making your food delivery app more efficient, reliable, and easier to maintain. Whether you’re handling authentication, uploading files, or dealing with concurrent requests, Axios provides the flexibility and control needed to manage these tasks effectively.

With the concepts and examples covered in this guide, you’re now equipped to build and enhance your food delivery app, ensuring it’s robust and ready to handle real-world demands.

Happy coding🎉!

Top comments (6)

Collapse
 
cookiemonsterdev profile image
Mykhailo Toporkov 🇺🇦 • Edited

Axios has some disadvantages, and the most critical is that it is much slover than fetch API.

Slower, it's because it uses XHR under the hood in the browser. Yeah, it is helpful for cases when you need the older browsers suppor. I would recommend recommend to use at least ky instead of axios, for better performance, though all mentioned features can be ease replicated by pure js.

Collapse
 
ankitakanchan profile image
Ankita Kanchan

Thank you for sharing your thoughts! You're right, Axios can be a bit slower because of XHR, but it's handy for older browser support. I'll definitely give ky a try to see how it compares. Always good to explore new tools!

Collapse
 
jayantbh profile image
Jayant Bhawal

Is the performance difference noticeable against any average APIs response time?

IMO given that an average API may take between 100-500ms to respond, axios being slower should not be the optimization devs should be focusing on.

There's other good reasons instead, such as fetch being capable enough and being supported without packages in both browser and nodejs.

Collapse
 
webjose profile image
José Pablo Ramírez Vargas

The reasons listed to select axios is like traveling back in time 20 years. The fact is that fetch covers everything easily. The one thing that doesn't provide is interceptors, and that can very easily be programmed in pure JS.

By now, axios should be a legacy package.

Collapse
 
peeleebee profile image
Phillip Anerine

Axios was cool, but with Fetch api standardized, not really a purpose to add axios to new things, as much as for maintaining old things before fetch.

Collapse
 
bbfkfj_hhdhd_ceac8c32bcca profile image
Info Comment hidden by post author - thread only accessible via permalink
Bbfkfj Hhdhd

Auspicious Capital Loan App Customer. Care Helpline Number =(91) ((7029672438 ))- ((8250927608)).

Some comments have been hidden by the post's author - find out more