How to Create React CRUD App with Redux RTK Endpoints

Last updated on: by Digamber

In this detailed guide, you will learn how to create CRUD operations in React Redux application using RTK Query and Api Slice.

We will explain how to build CRUD endpoints using RTK endpoint queries and how to call api in react-redux. How to fetch data from Redux RTK api slice, and how to use Redux toolkit’s latest or updated technique in React js.

CRUD is an acronym originating from the computer programming world, which means the main four functions responsible for creating, reading, updating, and deleting data from the database.

RTK Query is an additional package that comes in the Redux Toolkit library, and its functionality is built on top of the other APIs in Redux Toolkit.

React Build CRUD Operations using RTK Query Endpoints Tutorial

  • Step 1: Install React App
  • Step 2: Install Required Libraries
  • Step 3: Set Up Backend Server
  • Step 4: Build Redux CRUD Endpoints
  • Step 5: Add ApiSlice to Redux Store
  • Step 6: Inject ApiProvider in React
  • Step 7: Implement CRUD Operations
  • Step 8: Register Component in React
  • Step 9: Run React Project

Install React App

Head over to the terminal, add the given command on the console, and hit enter to download the app.

npx create-react-app react-redux-rtk-crud-example

Now, enter inside the app directory.

cd react-redux-rtk-crud-example

Install Required Libraries

Open the command prompt of the terminal app, type the given command and install the libraries we needed for this tutorial.

npm install react-redux @reduxjs/toolkit bootstrap

Set Up Backend Server

In this step, we will install the json-server library, which will give us the privilege to build a mock server and allow us to perform crud operations in the react-redux application.

npm install -g json-server

As soon as the module installed, make the db.json file with some dummy data.

{
  "posts": [
    {
      "id": 1,
      "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
      "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
    }
  ]
}

Start the mock api server that we built using json-server.

json-server --watch db.json

Build Redux CRUD Endpoints

We have to create the file and folder for creating api slice, make sure to remember you must create single api slice per base url in react redux.

Create the feature/api folders, next create the features/api/apiSlice.js file and add given code to the file.

import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
export const apiSlice = createApi({
  reducerPath: 'apiSlice',
  baseQuery: fetchBaseQuery({
    baseUrl: 'http://localhost:3000',
  }),
  tagTypes: ['Post'],
  endpoints: (builder) => ({
    getPosts: builder.query({
      query: () => '/posts',
      providesTags: ['Post'],
    }),
    addNewPost: builder.mutation({
      query: (payload) => ({
        url: '/posts',
        method: 'POST',
        body: payload,
        headers: {
          'Content-type': 'application/json; charset=UTF-8',
        },
      }),
      invalidatesTags: ['Post'],
    }),
    updatePost: builder.mutation({
      query: (payload) => {
        console.log(payload)
        const { id, ...body } = payload
        return {
          url: `/posts/${id}`,
          method: 'PUT',
          body,
        }
      },
      invalidatesTags: ['Post'],
    }),
    deletePost: builder.mutation({
      query: (id) => ({
        url: `/posts/${id}`,
        method: 'DELETE',
        credentials: 'include',
      }),
      invalidatesTags: ['Post'],
    }),
  }),
})
export const {
  useGetPostsQuery,
  useAddNewPostMutation,
  useUpdatePostMutation,
  useDeletePostMutation,
} = apiSlice

The following code example produces four autogenerated hooks we are getting from the endpoints. An endpoint describes how to fetch all the records, add a new record, delete records, and update records on the database.

Add ApiSlice to Redux Store

Create the store.js file in the src/app folder, then set up the redux store using configureStore and setupListeners modules and set the api slice in the reducer object.

import { configureStore } from '@reduxjs/toolkit'
import { setupListeners } from '@reduxjs/toolkit/query'
import { apiSlice } from '../features/api/apiSlice'
export const store = configureStore({
  reducer: {
    [apiSlice.reducerPath]: apiSlice.reducer,
  },
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware().concat(apiSlice.middleware),
})
setupListeners(store.dispatch)

Inject ApiProvider in React

In this part of the tutorial, we will see how to inject ApiProvider in React app. You need to go to the index.js file here first, import the ApiProvider and api slice, wrap the App component using ApiProvider and pass apiSlice to api value.

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import { ApiProvider } from '@reduxjs/toolkit/dist/query/react'
import { apiSlice } from './features/api/apiSlice'
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
  <React.StrictMode>
    <ApiProvider api={apiSlice}>
      <App />
    </ApiProvider>
  </React.StrictMode>,
)

Implement CRUD Operations

In this step, you will learn how to use Redux RTK hooks in React component to create, read, delete and update the data from the server.

Hence, create the features/posts/PostsList.js folder and file and add given code to the file.

import React, { useState } from 'react'
import {
  useGetPostsQuery,
  useAddNewPostMutation,
  useUpdatePostMutation,
  useDeletePostMutation,
} from '../api/apiSlice'
function PostsList() {
  const [addNewPost, response] = useAddNewPostMutation()
  const [deletePost] = useDeletePostMutation()
  const [inputField, setInputField] = useState({
    id: '',
    title: '',
    body: '',
  })
  const inputsHandler = (e) => {
    setInputField((prevState) => ({
      ...prevState,
      [e.target.name]: e.target.value,
    }))
  }
  const [updatePost, { isLoading: isUpdating }] = useUpdatePostMutation()
  const setPostData = (data) => {
    setInputField({
      id: data.id,
      title: data.title,
      body: data.body,
    })
  }
  const onEditData = () => {
    updatePost({
      id: inputField.id,
      title: inputField.title,
      body: inputField.body,
    })
    setInputField(() => ({
      id: '',
      title: '',
      body: '',
    }))
  }
  const onSubmit = (e) => {
    e.preventDefault()
    const { title, body } = e.target.elements
    setInputField((inputField) => ({
      ...inputField,
      [e.target.name]: e.target.value,
    }))
    let formData = {
      title: title.value,
      body: body.value,
    }
    addNewPost(formData)
      .unwrap()
      .then(() => {
        setInputField(() => ({
          id: '',
          title: '',
          body: '',
        }))
      })
      .then((error) => {
        console.log(error)
      })
  }
  const {
    data: posts,
    isLoading: isGetLoading,
    isSuccess: isGetSuccess,
    isError: isGetError,
    error: getError,
  } = useGetPostsQuery({ refetchOnMountOrArgChange: true })
  let postContent
  if (isGetLoading) {
    postContent = (
      <div className="d-flex justify-content-center">
        <div className="spinner-border" role="status">
          <span className="visually-hidden">Loading...</span>
        </div>
      </div>
    )
  } else if (isGetSuccess) {
    postContent = posts.map((item) => {
      return (
        <div className="col-lg-12 mb-3" key={item.id}>
          <div className="card alert alert-secondary">
            <div className="card-body">
              <h5 className="card-title">{item.title}</h5>
              <p className="card-text">{item.body}</p>
              <button
                onClick={() => deletePost(item.id)}
                className="btn btn-outline-danger me-2"
              >
                Remove
              </button>
              <button
                onClick={() => setPostData(item)}
                className="btn btn-outline-primary"
              >
                Edit
              </button>
            </div>
          </div>
        </div>
      )
    })
  } else if (isGetError) {
    postContent = (
      <div className="alert alert-danger" role="alert">
        {getError}
      </div>
    )
  }
  return (
    <div className="row">
      <div className="col-md-4 offset-md-*">
        <form onSubmit={onSubmit}>
          <div className="mb-3">
            <label className="form-label">
              <strong>Enter Title</strong>
            </label>
            <input
              value={inputField.title}
              type="text"
              className="form-control"
              name="title"
              id="title"
              onChange={inputsHandler}
            />
          </div>
          <div className="mb-3">
            <label className="form-label">
              <strong>Enter content</strong>
            </label>
            <textarea
              value={inputField.body}
              className="form-control"
              rows="3"
              name="body"
              id="body"
              onChange={inputsHandler}
            ></textarea>
          </div>
          <button className="btn btn-danger me-2" type="submit">
            Submit
          </button>
          <button
            onClick={onEditData}
            className="btn btn-primary"
            type="button"
          >
            Update
          </button>
        </form>
      </div>
      <div className="col-lg-8">
        <div className="row">{postContent}</div>
      </div>
    </div>
  )
}
export default PostsList

Register Component in React

App component is the core component in React. It is often considered a container for other components. Head over to the src/App.js file and add the given code in this file.

import '../node_modules/bootstrap/dist/css/bootstrap.min.css'
import './App.css'
import PostsList from './features/posts/PostsList'
function App() {
  return (
    <div className="container">
      <h2 className="mb-5">React RTK Query CRUD Operations Example</h2>
      <PostsList />
    </div>
  )
}
export default App

Run React Project

WE have eventually reached the final step of this guide, and we will test React Redux crud example app using the given command.

npm start

We are running our json server on a 3000 port; make sure to open the app on another port.

http://localhost:3001

How to Create React CRUD App with Redux RTK Endpoints

Conclusion

RTK query offers a great mechanism for data management and data caching. It is created to make the api interaction less painful in the web application development realm.

This guide taught us how to create CRUD operations endpoints in the Redux store using the RTK Query endpoints. Not only but also, we showed you how to consume RTK query CRUD endpoints in React component using RTK Query crud hooks.

Download the full code of this tutorial from GitRepo.

Digamber

A Full-stack developer with a passion to solve real world problems through functional programming.