In this tutorial, I will show you how to build a React Query and Axios example working with Rest API, display and modify data (CRUD operations) with Hooks.
Related Posts:
– React Custom Hook
– React Hooks (without React Query) example with Axios and Rest API
– React Hooks File Upload example with Axios & Progress Bar
– React Table example: CRUD App | react-table 7
– React Form Validation with Hooks example
Security:
– React Hooks: JWT Authentication (without Redux) example
– React Hooks + Redux: JWT Authentication example
Serverless with Firebase:
– React Hooks + Firebase Realtime Database: CRUD App
– React Hooks + Firestore example: CRUD app
Typescript version: React Query and Axios (Typescript) example with Rest API
Contents
React Query overview
Most state management libraries (including Redux) are good for working with client state, but not for server state. It’s because server state is persisted remotely in a location the client side cannot control, it can become outdate in our applications and we need to make asynchronous APIs for fetching and updating.
React Query is one of the best libraries for managing server state. It helps us fetch, cache, synchronize and update data without touching any global state.
React Query helps us:
- remove complicated and misunderstood code and replace with several React Query logic
- easier to maintain and build new features without worrying about wiring up new server state data sources
- make our application feel faster and more responsive
- save bandwidth and increase memory performance
React Query and Axios example
We will build a React Client with React Query and Axios library to make CRUD requests to Rest API in that:
- React Query Axios GET request: get all Tutorials, get Tutorial by Id, find Tutorial by title
- React Query Axios POST request: create new Tutorial
- React Query Axios PUT request: update an existing Tutorial
- React Query Axios DELETE request: delete a Tutorial, delete all Tutorials
This React Query Axios Client works with the following Web API:
Methods | Urls | Actions |
---|---|---|
POST | /api/tutorials | create new Tutorial |
GET | /api/tutorials | retrieve all Tutorials |
GET | /api/tutorials/:id | retrieve a Tutorial by :id |
PUT | /api/tutorials/:id | update a Tutorial by :id |
DELETE | /api/tutorials/:id | delete a Tutorial by :id |
DELETE | /api/tutorials | delete all Tutorials |
GET | /api/tutorials?title=[keyword] | find all Tutorials which title contains keyword |
You can find step by step to build a Server like this in one of these posts:
- Express, Sequelize & MySQL
- Express, Sequelize & PostgreSQL
- Express, Sequelize & SQL Server
- Express & MongoDb
- Spring Boot & MySQL
- Spring Boot & PostgreSQL
- Spring Boot & MongoDB
- Spring Boot & SQL Server
- Spring Boot & H2
- Spring Boot & Cassandra
- Spring Boot & Oracle
- Django & MySQL
- Django & PostgreSQL
- Django & MongoDB
Access-Control-Allow-Origin: *
.It helps the REST APIs can be accessed by any origin.
Setup React Query and Axios
React Query
Install React Query module:
- Using npm:
$ npm install react-query
$ yarn add react-query
Let’s create new QueryClient
to interact with a cache.
Open index.js, wrap App component with QueryClientProvider component which connects and provides QueryClient
object to our application.
src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
...
import { QueryClient, QueryClientProvider } from "react-query";
const queryClient = new QueryClient();
ReactDOM.render(
<React.StrictMode>
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
</React.StrictMode>,
document.getElementById('root')
);
...
From official Document Important Defaults section, React Query will consider cached data as stale. Stale queries are re-fetched automatically in the background when:
- New instances of the query mount
- The window is refocused
- The network is reconnected
- The query is optionally configured with a refetch interval.
You can turn off most of the defaults by passing defaultOptions
as config
parameter. For example:
const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
refetchOnmount: false,
refetchOnReconnect: false,
retry: false,
staleTime: 5*60*1000,
},
},
});
refetchOnWindowFocus
: automatically requests fresh data in the background if user leaves the app and returns to stale data.refetchOnmount
: iftrue
, refetch on mount if the data is stale.refetchOnReconnect
: iftrue
, refetch on reconnect if the data is stale.retry
: iftrue
, failed queries will retry infinitely.staleTime
: the time in milliseconds after data is considered stale. Defaults to0
.
Axios
Install axios module:
- Using npm:
$ npm install [email protected]
$ yarn add [email protected]
We can create a new instance of axios using axios.create(config)
method, then export it as an apiClient
:
src/http-common.js
import axios from "axios";
export default axios.create({
baseURL: "http://localhost:8080/api",
headers: {
"Content-type": "application/json"
}
});
In src/App.js
import apiClient from "./http-common";
...
Now we can use apiClient
to send HTTP requests and receive responses.
The response for a Axios request contains:
data
: parsed response body provided by the serverstatus
: HTTP status codestatusText
: HTTP status messageheaders
: HTTP headers (lower case)config
: the request config that was provided toaxios
request
: the last client request instance that generated this response
Axios Tutorial: Get/Post/Put/Delete request example
React Query Axios GET
To fetch JSON data from API, we use React Query useQuery hook:
Now look at following simple example:
import { useQuery } from 'react-query'
function App() {
const { isLoading, isSuccess, isError, data, error, refetch } =
useQuery('query-tutorials', fetchTutorials, { enabled: false, retry: 2, onSuccess, onError });
}
– 'query-tutorials'
(queryKey
) (Required): The query will automatically update when this key changes (in case enabled
is not set to false
). If you want to run the query everytime title
changes, you can use queryKey
like this: ['query-tutorials', title]
.
– fetchTutorials
(queryFn) is async function that returns a Promise.
– enabled
: if false
, disable this query from automatically running.
– retry
: if true
, failed queries will retry infinitely. If set to a number
, failed queries will retry that number of times. By default, queries are silently retried 3 times, with exponential backoff delay before capturing and displaying an error to the UI.
– onSuccess
: the callback function that will fire any time the query successfully fetches new data.
– onError
: the callback function that will fire if the query encounters an error and will be passed the error.
– useQuery
result contains:
isLoading
:true
if there is no cached data and the query is currently fetching.isSuccess
:true
if the query has received a response with no errors and ready to display data,data
property is the data received from the successful fetch.isError
:true
if the query has failed with an error.error
property has the error received from the attempted fetch.refetch
: the function to manually refetch the query.
There are many properties that you can find at useQuery Reference.
Let’s implement a React component with React Query and Axios that can:
- get all Tutorials
- get Tutorial by Id
- find Tutorial by title
import React, { useState, useEffect } from "react";
import { useQuery } from "react-query";
import apiClient from "./http-common";
function App() {
const [getId, setGetId] = useState("");
const [getTitle, setGetTitle] = useState("");
const [getResult, setGetResult] = useState(null);
const fortmatResponse = (res) => {
return JSON.stringify(res, null, 2);
};
const { isLoading: isLoadingTutorials, refetch: getAllTutorials } = useQuery(
"query-tutorials",
async () => {
return await apiClient.get("/tutorials");
},
{
enabled: false,
onSuccess: (res) => {
const result = {
status: res.status + "-" + res.statusText,
headers: res.headers,
data: res.data,
};
setGetResult(fortmatResponse(result));
},
onError: (err) => {
setGetResult(fortmatResponse(err.response?.data || err));
},
}
);
useEffect(() => {
if (isLoadingTutorials) setGetResult("loading...");
}, [isLoadingTutorials]);
function getAllData() {
try {
getAllTutorials();
} catch (err) {
setGetResult(fortmatResponse(err));
}
}
const { isLoading: isLoadingTutorial, refetch: getTutorialById } = useQuery(
"query-tutorial-by-id",
async () => {
return await apiClient.get(`/tutorials/${getId}`);
},
{
enabled: false,
retry: 1,
onSuccess: (res) => {
const result = {
status: res.status + "-" + res.statusText,
headers: res.headers,
data: res.data,
};
setGetResult(fortmatResponse(result));
},
onError: (err) => {
setGetResult(fortmatResponse(err.response?.data || err));
},
}
);
useEffect(() => {
if (isLoadingTutorial) setGetResult("loading...");
}, [isLoadingTutorial]);
function getDataById() {
if (getId) {
try {
getTutorialById();
} catch (err) {
setGetResult(fortmatResponse(err));
}
}
}
const { isLoading: isSearchingTutorial, refetch: findTutorialsByTitle } =
useQuery(
"query-tutorials-by-title", // ["query-tutorials-by-title", getTitle],
async () => {
return await apiClient.get(`/tutorials?title=${getTitle}`);
},
{
enabled: false,
retry: 1,
onSuccess: (res) => {
const result = {
status: res.status + "-" + res.statusText,
headers: res.headers,
data: res.data,
};
setGetResult(fortmatResponse(result));
},
onError: (err) => {
setGetResult(fortmatResponse(err.response?.data || err));
},
}
);
useEffect(() => {
if (isSearchingTutorial) setGetResult("searching...");
}, [isSearchingTutorial]);
function getDataByTitle() {
if (getTitle) {
try {
findTutorialsByTitle();
} catch (err) {
setGetResult(fortmatResponse(err));
}
}
}
const clearGetOutput = () => {
setGetResult(null);
};
return (
<div id="app" className="container">
<div className="card">
<div className="card-header">React Query Axios GET - BezKoder.com</div>
<div className="card-body">
<div className="input-group input-group-sm">
<button className="btn btn-sm btn-primary" onClick={getAllData}>
Get All
</button>
<input
type="text"
value={getId}
onChange={(e) => setGetId(e.target.value)}
className="form-control ml-2"
placeholder="Id"
/>
<div className="input-group-append">
<button className="btn btn-sm btn-primary" onClick={getDataById}>
Get by Id
</button>
</div>
<input
type="text"
value={getTitle}
onChange={(e) => setGetTitle(e.target.value)}
className="form-control ml-2"
placeholder="Title"
/>
<div className="input-group-append">
<button
className="btn btn-sm btn-primary"
onClick={getDataByTitle}
>
Find By Title
</button>
</div>
<button
className="btn btn-sm btn-warning ml-2"
onClick={clearGetOutput}
>
Clear
</button>
</div>
{getResult && (
<div className="alert alert-secondary mt-2" role="alert">
<pre>{getResult}</pre>
</div>
)}
</div>
</div>
</div>
);
}
export default App;
The result will look like this:
Find tutorial by id:
Filter tutorials by title:
React Query Axios POST
To perform CREATE, UPDATE, or DELETE operations inside our React component, we use React Query useMutation hook.
import { useMutation } from 'react-query'
function App() {
const { isLoading, isSuccess, isError, data, error, mutate } =
useMutation(postTutorial(info), { onSuccess, onError });
}
– postTutorial
(mutationFn) is async function that returns a Promise.
– onSuccess
: the callback function that will fire any time the query successfully fetches new data.
– onError
: the callback function that will fire if the query encounters an error and will be passed the error.
– useMutation
result contains:
isLoading
:true
if there is no cached data and the query is currently fetching.isSuccess
:true
if the query has received a response with no errors and ready to display data,data
property is the data received from the successful fetch.isError
:true
if the query has failed with an error.error
property has the error received from the attempted fetch.mutate
: the function (with variables) to manually to trigger the mutation.
There are many properties that you can find at useMutation Reference.
Let’s use React Query with Axios POST request to create new Tutorial.
import React, { useState, useEffect } from "react";
import { useMutation } from "react-query";
import apiClient from "./http-common";
function App() {
const [postTitle, setPostTitle] = useState("");
const [postDescription, setPostDescription] = useState("");
const [postResult, setPostResult] = useState(null);
const fortmatResponse = (res) => {
return JSON.stringify(res, null, 2);
};
const { isLoading: isPostingTutorial, mutate: postTutorial } = useMutation(
async () => {
return await apiClient.post(`/tutorials`, {
title: postTitle,
description: postDescription,
});
},
{
onSuccess: (res) => {
const result = {
status: res.status + "-" + res.statusText,
headers: res.headers,
data: res.data,
};
setPostResult(fortmatResponse(result));
},
onError: (err) => {
setPostResult(fortmatResponse(err.response?.data || err));
},
}
);
useEffect(() => {
if (isPostingTutorial) setPostResult("posting...");
}, [isPostingTutorial]);
function postData() {
try {
postTutorial();
} catch (err) {
setPostResult(fortmatResponse(err));
}
}
const clearPostOutput = () => {
setPostResult(null);
};
return (
<div id="app" className="container">
<div className="card">
<div className="card-header">React Query Axios POST - BezKoder.com</div>
<div className="card-body">
<div className="form-group">
<input
type="text"
value={postTitle}
onChange={(e) => setPostTitle(e.target.value)}
className="form-control"
placeholder="Title"
/>
</div>
<div className="form-group">
<input
type="text"
value={postDescription}
onChange={(e) => setPostDescription(e.target.value)}
className="form-control"
placeholder="Description"
/>
</div>
<button className="btn btn-sm btn-primary" onClick={postData}>
Post Data
</button>
<button
className="btn btn-sm btn-warning ml-2"
onClick={clearPostOutput}
>
Clear
</button>
{postResult && (
<div className="alert alert-secondary mt-2" role="alert">
<pre>{postResult}</pre>
</div>
)}
</div>
</div>
</div>
);
}
export default App;
Check the result by making a React Query Axios Post Request:
React Query Axios PUT
We’re gonna use React Query with Axios PUT request to update an existing Tutorial.
import React, { useState, useEffect } from "react";
import { useMutation } from "react-query";
import apiClient from "./http-common";
function App() {
const [putId, setPutId] = useState("");
const [putTitle, setPutTitle] = useState("");
const [putDescription, setPutDescription] = useState("");
const [putPublished, setPutPublished] = useState(false);
const [putResult, setPutResult] = useState(null);
const fortmatResponse = (res) => {
return JSON.stringify(res, null, 2);
};
const { isLoading: isUpdatingTutorial, mutate: updateTutorial } = useMutation(
async () => {
return await apiClient.put(`/tutorials/${putId}`, {
title: putTitle,
description: putDescription,
published: putPublished,
});
},
{
onSuccess: (res) => {
const result = {
status: res.status + "-" + res.statusText,
headers: res.headers,
data: res.data,
};
setPutResult(fortmatResponse(result));
},
onError: (err) => {
setPutResult(fortmatResponse(err.response?.data || err));
},
}
);
useEffect(() => {
if (isUpdatingTutorial) setPutResult("updating...");
}, [isUpdatingTutorial]);
function putData() {
if (putId) {
try {
updateTutorial();
} catch (err) {
setPutResult(fortmatResponse(err));
}
}
}
const clearPutOutput = () => {
setPutResult(null);
};
return (
<div id="app" className="container">
<div className="card">
<div className="card-header">React Query Axios PUT - BezKoder.com</div>
<div className="card-body">
<div className="form-group">
<input
type="text"
value={putId}
onChange={(e) => setPutId(e.target.value)}
className="form-control"
placeholder="Id"
/>
</div>
<div className="form-group">
<input
type="text"
value={putTitle}
onChange={(e) => setPutTitle(e.target.value)}
className="form-control"
placeholder="Title"
/>
</div>
<div className="form-group">
<input
type="text"
value={putDescription}
onChange={(e) => setPutDescription(e.target.value)}
className="form-control"
placeholder="Description"
/>
</div>
<div className="form-check mb-2">
<input
type="checkbox"
name="putPublished"
checked={putPublished}
onChange={(e) => setPutPublished(e.target.checked)}
className="form-check-input"
/>
<label className="form-check-label" htmlFor="putPublished">
Publish
</label>
</div>
<button className="btn btn-sm btn-primary" onClick={putData}>
Update Data
</button>
<button
className="btn btn-sm btn-warning ml-2"
onClick={clearPutOutput}
>
Clear
</button>
{putResult && (
<div className="alert alert-secondary mt-2" role="alert">
<pre>{putResult}</pre>
</div>
)}
</div>
</div>
</div>
);
}
export default App;
The result will look like this:
React Query Axios DELETE
Now we implement a React component to delete data with React Query and Axios Delete method:
- delete a Tutorial
- delete all Tutorials
import React, { useState, useEffect } from "react";
import { useMutation } from "react-query";
import apiClient from "./http-common";
function App() {
const [deleteId, setDeleteId] = useState("");
const [deleteResult, setDeleteResult] = useState(null);
const fortmatResponse = (res) => {
return JSON.stringify(res, null, 2);
};
const { isLoading: isDeletingTutorials, mutate: deleteAllTutorials } =
useMutation(
async () => {
return await apiClient.delete("/tutorials/");
},
{
onSuccess: (res) => {
const result = {
status: res.status + "-" + res.statusText,
headers: res.headers,
data: res.data,
};
setDeleteResult(fortmatResponse(result));
},
onError: (err) => {
setDeleteResult(fortmatResponse(err.response?.data || err));
},
}
);
useEffect(() => {
if (isDeletingTutorials) setDeleteResult("deleting...");
}, [isDeletingTutorials]);
function deleteAllData() {
try {
deleteAllTutorials();
} catch (err) {
setDeleteResult(fortmatResponse(err));
}
}
const { isLoading: isDeletingTutorial, mutate: deleteTutorial } = useMutation(
async () => {
return await apiClient.delete(`/tutorials/${deleteId}`);
},
{
onSuccess: (res) => {
const result = {
status: res.status + "-" + res.statusText,
headers: res.headers,
data: res.data,
};
setDeleteResult(fortmatResponse(result));
},
onError: (err) => {
setDeleteResult(fortmatResponse(err.response?.data || err));
},
}
);
useEffect(() => {
if (isDeletingTutorial) setDeleteResult("deleting...");
}, [isDeletingTutorial]);
function deleteDataById() {
if (deleteId) {
try {
deleteTutorial();
} catch (err) {
setDeleteResult(fortmatResponse(err));
}
}
}
const clearDeleteOutput = () => {
setDeleteResult(null);
};
return (
<div id="app" className="container">
<div className="card">
<div className="card-header">
React Query Axios DELETE - BezKoder.com
</div>
<div className="card-body">
<div className="input-group input-group-sm">
<button className="btn btn-sm btn-danger" onClick={deleteAllData}>
Delete All
</button>
<input
type="text"
value={deleteId}
onChange={(e) => setDeleteId(e.target.value)}
className="form-control ml-2"
placeholder="Id"
/>
<div className="input-group-append">
<button
className="btn btn-sm btn-danger"
onClick={deleteDataById}
>
Delete by Id
</button>
</div>
<button
className="btn btn-sm btn-warning ml-2"
onClick={clearDeleteOutput}
>
Clear
</button>
</div>
{deleteResult && (
<div className="alert alert-secondary mt-2" role="alert">
<pre>{deleteResult}</pre>
</div>
)}
</div>
</div>
</div>
);
}
export default App;
The result could be like this:
Conclusion
With this React Query and Axios example, you’ve known many ways to make GET/POST/PUT/DELETE request using react-query and axios library in a Reactjs component.
If you don’t want to use React Query, just Axios. Kindly visit
React Axios example – Get/Post/Put/Delete with Rest API
Instead of Axios, you can also use Javascript Fetch API:
React Fetch example – Get/Post/Put/Delete with Rest API
Or build your custom Hooks:
React Custom Hook
Happy Learning! See you again.
Source Code
The complete source code for this tutorial can be found at Github.
Typescript version: React Query and Axios (Typescript) example with Rest API
Further Reading
- Axios Tutorial: Get/Post/Put/Delete request example
- React CRUD example with Axios, React Router and Rest API
- React Hooks: JWT Authentication & Authorization (without Redux) example
- React Hooks + Redux: JWT Authentication & Authorization example
Dear,
Great tutorials.
It will be a good idea to introduce also graphQL (Query – mutations) instead of AXIOS?
Thanks a lot for the tutorial.