In today’s interconnected digital world, applications frequently need to communicate with external servers to fetch data, submit information, and interact with various services. This interaction typically happens through APIs (Application Programming Interfaces). As a developer, efficiently handling these API requests is crucial for building responsive and robust applications. This is where Axios, a popular promise-based HTTP client, combined with the power of TypeScript, comes into play. This tutorial will guide you through the process of using Axios in TypeScript to make API requests, handle responses, and manage errors effectively. We’ll explore various request methods, data serialization, and best practices to ensure your applications can seamlessly interact with APIs.
Why TypeScript and Axios?
Before diving into the code, let’s understand why TypeScript and Axios are a winning combination:
- TypeScript: TypeScript brings static typing to JavaScript, which helps you catch errors early in the development process. It provides better code organization, improved readability, and enhanced maintainability. With TypeScript, you can define the structure of your data (API responses, request bodies, etc.), making your code more predictable and less prone to runtime errors.
- Axios: Axios is a lightweight HTTP client that simplifies the process of making API requests from your browser or Node.js. It offers a clean and intuitive API, supports features like request and response interception, and handles common tasks such as automatically transforming JSON data.
By using TypeScript with Axios, you get the benefits of type safety and a user-friendly HTTP client, leading to more reliable and maintainable code.
Setting Up Your Project
To get started, you’ll need to set up a TypeScript project and install Axios. If you don’t have Node.js and npm (or yarn) installed, you’ll need to install them first. Then, follow these steps:
- Create a Project Directory: Create a new directory for your project and navigate into it using your terminal:
mkdir typescript-axios-tutorial
cd typescript-axios-tutorial
- Initialize a Node.js Project: Initialize a new Node.js project by running:
npm init -y
- Install TypeScript and Axios: Install TypeScript and Axios as development dependencies:
npm install typescript axios --save-dev
- Initialize TypeScript: Initialize a TypeScript configuration file (tsconfig.json):
npx tsc --init
This command creates a tsconfig.json file in your project. You might want to modify this file to suit your project’s needs. For example, you can change the outDir to specify where the compiled JavaScript files should be placed.
- Create a TypeScript File: Create a TypeScript file (e.g.,
index.ts) where you’ll write your code.
Making GET Requests
Let’s start with a simple GET request. We’ll use a public API (e.g., JSONPlaceholder) to fetch some data. Here’s how you can do it:
// index.ts
import axios from 'axios';
async function fetchData() {
try {
const response = await axios.get('https://jsonplaceholder.typicode.com/todos/1');
console.log(response.data);
// Access specific properties
console.log('Title:', response.data.title);
} catch (error) {
console.error('Error fetching data:', error);
}
}
fetchData();
In this code:
- We import Axios.
- We define an asynchronous function
fetchData. - We use
axios.get()to make a GET request to the specified URL. - We use
awaitto wait for the response. - We log the response data to the console.
- We wrap the request in a
try...catchblock to handle potential errors.
To run this code, compile it using the TypeScript compiler:
tsc index.ts
Then, execute the compiled JavaScript file using Node.js:
node index.js
You should see the data from the API printed in your console.
Working with Response Types
One of the significant advantages of TypeScript is type safety. Let’s define an interface to represent the structure of the data we expect to receive from the API:
// index.ts
import axios from 'axios';
interface Todo {
userId: number;
id: number;
title: string;
completed: boolean;
}
async function fetchData(): Promise<void> {
try {
const response = await axios.get<Todo>('https://jsonplaceholder.typicode.com/todos/1');
// TypeScript knows the structure of the response.data
console.log('Title:', response.data.title);
} catch (error) {
console.error('Error fetching data:', error);
}
}
fetchData();
In this updated code:
- We define an interface
Todothat describes the expected structure of the todo item. - We use the generic type
<Todo>withaxios.get()to specify the expected type of the response data. - TypeScript now knows the structure of
response.data, providing type checking and autocompletion in your editor.
This approach helps prevent errors and makes your code more maintainable.
Making POST Requests
To send data to an API, you’ll use the POST method. Here’s an example:
// index.ts
import axios from 'axios';
interface Todo {
userId: number;
title: string;
completed: boolean;
}
async function createTodo(todo: Todo): Promise<void> {
try {
const response = await axios.post<Todo>('https://jsonplaceholder.typicode.com/todos', todo);
console.log('Created todo:', response.data);
} catch (error) {
console.error('Error creating todo:', error);
}
}
const newTodo: Todo = {
userId: 1,
title: 'Buy groceries',
completed: false,
};
createTodo(newTodo);
In this example:
- We define a
createTodofunction that takes aTodoobject as input. - We use
axios.post()to make a POST request to the API endpoint. The second argument toaxios.post()is the data to send. - We pass the
todoobject as the request body. - We specify the response type as
<Todo>.
Making PUT and PATCH Requests
PUT and PATCH requests are used for updating existing resources. PUT typically replaces the entire resource, while PATCH updates only specific fields. Here’s an example of a PUT request:
// index.ts
import axios from 'axios';
interface Todo {
userId: number;
id: number;
title: string;
completed: boolean;
}
async function updateTodo(id: number, updatedTodo: Partial<Todo>): Promise<void> {
try {
const response = await axios.put<Todo>(`https://jsonplaceholder.typicode.com/todos/${id}`, updatedTodo);
console.log('Updated todo:', response.data);
} catch (error) {
console.error('Error updating todo:', error);
}
}
const todoIdToUpdate = 1;
const updates: Partial<Todo> = {
title: 'Wash the car',
completed: true,
};
updateTodo(todoIdToUpdate, updates);
And here’s an example of a PATCH request:
// index.ts
import axios from 'axios';
interface Todo {
userId: number;
id: number;
title: string;
completed: boolean;
}
async function patchTodo(id: number, updates: Partial<Todo>): Promise<void> {
try {
const response = await axios.patch<Todo>(`https://jsonplaceholder.typicode.com/todos/${id}`, updates);
console.log('Patched todo:', response.data);
} catch (error) {
console.error('Error patching todo:', error);
}
}
const todoIdToPatch = 1;
const patchUpdates: Partial<Todo> = {
completed: true,
};
patchTodo(todoIdToPatch, patchUpdates);
In these examples:
- We define
updateTodo(PUT) andpatchTodo(PATCH) functions. - We use
axios.put()andaxios.patch(), respectively, to send the requests. Note the use of template literals to include the `id` in the URL. - We use the
Partial<Todo>type to allow updating only specific properties of theTodoobject.
Making DELETE Requests
To delete a resource, you’ll use the DELETE method:
// index.ts
import axios from 'axios';
async function deleteTodo(id: number): Promise<void> {
try {
await axios.delete(`https://jsonplaceholder.typicode.com/todos/${id}`);
console.log('Todo deleted');
} catch (error) {
console.error('Error deleting todo:', error);
}
}
const todoIdToDelete = 1;
deleteTodo(todoIdToDelete);
In this example:
- We define a
deleteTodofunction. - We use
axios.delete()to send the DELETE request. - We don’t expect any data in the response, so we don’t specify a response type.
Handling Errors
Proper error handling is essential for building robust applications. Axios provides several ways to handle errors. The most common approach is using try...catch blocks, as shown in the previous examples. However, you can also use .catch() on the promise returned by Axios.
// index.ts
import axios from 'axios';
async function fetchData() {
axios.get('https://jsonplaceholder.typicode.com/todos/9999') // Non-existent resource
.then(response => {
console.log(response.data);
})
.catch(error => {
if (error.response) {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
console.error('Status:', error.response.status);
console.error('Data:', error.response.data);
console.error('Headers:', error.response.headers);
} else if (error.request) {
// The request was made but no response was received
// `error.request` is an instance of XMLHttpRequest in the browser and an http.ClientRequest in node.js
console.error('Request error:', error.request);
} else {
// Something happened in setting up the request that triggered an Error
console.error('Error:', error.message);
}
});
}
fetchData();
In this example:
- We use
.then()to handle the successful response. - We use
.catch()to handle errors. - Inside the
.catch()block, we check the type of the error to handle different scenarios: error.response: The server responded with an error status code (e.g., 404, 500).error.request: The request was made, but no response was received (e.g., network error).- Other errors: Errors that occurred during the request setup.
Axios also provides the ability to intercept requests and responses, which can be useful for global error handling, adding authentication headers, and more. We will cover this in more detail later.
Request and Response Interceptors
Interceptors allow you to intercept and modify requests before they are sent and responses before they are handled. This is extremely useful for tasks like adding authentication headers, logging requests, or handling errors globally.
// index.ts
import axios from 'axios';
// Add a request interceptor
axios.interceptors.request.use(
config => {
// Do something before request is sent
console.log('Request Interceptor: Adding authorization header');
// For example, add an authorization header:
// config.headers.Authorization = 'Bearer YOUR_TOKEN';
return config;
},
error => {
// Do something with request error
return Promise.reject(error);
}
);
// Add a response interceptor
axios.interceptors.response.use(
response => {
// Any status code that lie within the range of 2xx cause this function to trigger
// Do something with response data
console.log('Response Interceptor: Response received');
return response;
},
error => {
// Any status codes that falls outside the range of 2xx cause this function to trigger
// Do something with response error
console.error('Response Interceptor: Handling error globally', error);
return Promise.reject(error);
}
);
async function fetchData() {
try {
const response = await axios.get('https://jsonplaceholder.typicode.com/todos/1');
console.log(response.data);
} catch (error) {
console.error('Error fetching data:', error);
}
}
fetchData();
In this example:
- We use
axios.interceptors.request.use()to add a request interceptor. This interceptor is called before each request is sent. We can modify the request configuration (config) before it’s sent. In the example, we show how you could add an authorization header. - We use
axios.interceptors.response.use()to add a response interceptor. This interceptor is called after a response is received. We can handle the response data or handle errors globally. - Both interceptors have a success callback and an error callback.
Customizing Axios Configuration
Axios allows you to configure various aspects of your requests, such as timeouts, headers, and base URLs. You can set these configurations globally or for individual requests.
// index.ts
import axios from 'axios';
// Global configuration
axios.defaults.baseURL = 'https://jsonplaceholder.typicode.com';
axios.defaults.timeout = 5000; // 5 seconds
axios.defaults.headers.common['Authorization'] = 'Bearer YOUR_TOKEN';
async function fetchData() {
try {
const response = await axios.get('/todos/1'); // Relative URL, baseURL is used
console.log(response.data);
} catch (error) {
console.error('Error fetching data:', error);
}
}
fetchData();
In this example:
- We set a global
baseURL,timeout, and anAuthorizationheader usingaxios.defaults. - When making a request, we can use a relative URL (
/todos/1) because thebaseURLis already defined.
You can also override these defaults for individual requests:
// index.ts
import axios from 'axios';
// Global configuration (as before)
axios.defaults.baseURL = 'https://jsonplaceholder.typicode.com';
async function fetchData() {
try {
const response = await axios.get('/todos/1', {
timeout: 10000, // Override the global timeout
});
console.log(response.data);
} catch (error) {
console.error('Error fetching data:', error);
}
}
fetchData();
Here, we override the global timeout for a specific request.
Working with Query Parameters
To include query parameters in your GET requests, you can use the params option:
// index.ts
import axios from 'axios';
interface Todo {
userId: number;
id: number;
title: string;
completed: boolean;
}
async function fetchTodosByUserId(userId: number): Promise<void> {
try {
const response = await axios.get<Todo[]>('/todos', {
params: {
userId: userId,
},
});
console.log(response.data);
} catch (error) {
console.error('Error fetching todos:', error);
}
}
fetchTodosByUserId(1);
In this example:
- We use the
paramsoption when callingaxios.get(). - We pass an object containing the query parameters (e.g.,
userId). Axios will automatically append these parameters to the URL.
Common Mistakes and How to Fix Them
Here are some common mistakes and how to avoid them:
- Incorrect API Endpoint: Double-check the API endpoint URL for typos.
- Incorrect Data Structure: Ensure your data structures (interfaces, types) match the API’s expected format. Use the browser’s developer tools (Network tab) or tools like Postman to inspect the API’s requests and responses.
- CORS Issues: If you’re making requests from a browser to a different domain, you might encounter CORS (Cross-Origin Resource Sharing) issues. Make sure the server you are requesting from has CORS configured correctly or use a proxy server during development.
- Missing Headers: Some APIs require specific headers (e.g.,
Content-Type,Authorization). Make sure you include the necessary headers in your requests. Use the browser’s developer tools or Postman to see what headers the API expects. - Incorrect Data Type in Request Body: Ensure that the data you send in the request body is in the correct format (e.g., JSON). If you’re sending form data, make sure to set the correct
Content-Typeheader (e.g.,application/x-www-form-urlencoded). - Not Handling Errors: Always handle errors using
try...catchblocks or.catch()to gracefully manage API failures. Log errors to the console or display them to the user.
Key Takeaways
- Axios is a powerful and easy-to-use HTTP client for making API requests in your TypeScript applications.
- TypeScript enhances code quality and maintainability by providing type safety.
- Use
axios.get(),axios.post(),axios.put(),axios.patch(), andaxios.delete()for different HTTP methods. - Define interfaces to represent the structure of your data.
- Use
try...catchblocks or.catch()to handle errors. - Leverage request and response interceptors for global error handling, adding headers, and more.
- Customize Axios configuration for specific requests or globally.
- Pay close attention to API documentation and data formats.
FAQ
Here are some frequently asked questions:
- Can I use Axios in a React application?
Yes, Axios is commonly used in React applications to make API requests. You can install it using npm or yarn and import it into your components.
- How do I handle authentication with Axios?
You can use request interceptors to add authentication headers (e.g., Bearer tokens) to your requests. Store the token securely (e.g., in local storage, a cookie, or a state management solution) and retrieve it in the interceptor.
- What is the difference between
axios.get(),axios.post(),axios.put(), andaxios.patch()?These methods correspond to different HTTP methods:
axios.get(): Retrieves data from the server.axios.post(): Sends data to the server to create a new resource.axios.put(): Sends data to the server to replace an existing resource entirely.axios.patch(): Sends data to the server to partially update an existing resource.
- How do I cancel an Axios request?
You can cancel an Axios request using an
AbortController. Create an instance ofAbortController, pass itssignalto the Axios request, and callabort()on the controller when you want to cancel the request.
Mastering the art of making API requests is a crucial skill for any modern web developer. By combining TypeScript’s type safety with Axios’s ease of use, you can build robust and maintainable applications that seamlessly interact with APIs. Remember to always handle errors, understand the API documentation, and use the techniques discussed in this tutorial to improve your development workflow. As you continue to build applications, you will find that a solid understanding of API interactions is essential for creating dynamic and engaging user experiences.
