API Setup in NextJs
Tags: javascript, node, nextjs, mern
Published: 2023-12-17 19:44:12

In this tutorial we have integrate other Api. Previously we did 1st part and 2nd part. If you don’t know then see this link

1. Making API Using Node and Express. [Link](URL)
2. Scaffolding redux-toolkit in NextJs. [Link](URL)

Now we are do 3rd part. Let’s derive on-

**Step-1 : Authentication Api Setup**
Go to reduxStore/features/auth folder and create this file
  1. authApi.js
  2. authSlice.js

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a56o6mqaj4sassn3nyee.png)

**authApi.js**

```
import { apiSlice } from "../api/apiSlice";
import { userLoggedIn } from "./authSlice";

export const authApi = apiSlice.injectEndpoints({
    endpoints: (builder) => ({
        register: builder.mutation({
            query: (data) => ({
                url: "/register",
                method: "POST",
                body: data,
            }),
            async onQueryStarted(arg, { queryFulfilled, dispatch }) {
                try {
                    const result = await queryFulfilled;

                    localStorage.setItem(
                        "auth",
                        JSON.stringify({
                            accessToken: result.data.accessToken,
                            user: result.data.user,
                        })
                    );

                    dispatch(
                        userLoggedIn({
                            accessToken: result.data.accessToken,
                            user: result.data.user,
                        })
                    );
                } catch (err) {
                    // do nothing
                }
            },
        }),
        login: builder.mutation({
            query: (data) => ({
                url: "/login",
                method: "POST",
                body: data,
            }),

            async onQueryStarted(arg, { queryFulfilled, dispatch }) {
                try {
                    const result = await queryFulfilled;
                    console.log('data fetch', result)
                    localStorage.setItem(
                        "auth",
                        JSON.stringify({
                            accessToken: result.data.payload.token,
                            user:result.data.payload,
                        })
                    );

                    dispatch(
                        userLoggedIn({
                            accessToken: result.data.payload.token,
                            user: result.data.payload,
                        })
                    );
                } catch (err) {
                    // do nothing
                }
            },
        }),
    }),
});

export const { useLoginMutation, useRegisterMutation } = authApi;

```
Here this authApi we have 2 end points. 
    1. "/register" for register user 
    2. "/login" for login user.

**authSlice.js**

```
import { createSlice } from "@reduxjs/toolkit";

const initialState = {
    accessToken: undefined,
    user: undefined,
};

const authSlice = createSlice({
    name: "auth",
    initialState,
    reducers: {
        userLoggedIn: (state, action) => {
            state.accessToken = action.payload.accessToken;
            state.user = action.payload.user;
        },
        userLoggedOut: (state) => {
            state.accessToken = undefined;
            state.user = undefined;
        },
    },
});

export const { userLoggedIn, userLoggedOut } = authSlice.actions;
export default authSlice.reducer;

```
Here this authSlice we have two actions. 
   1. userLoggedIn 
   2. userLoggedOut.

**Step-2 : Login page Setup and Others**
Go to login folder and create page.js file and do this

**page.js**

```
"use client";

import { useRouter } from 'next/navigation';
import { useEffect, useState } from "react";
import { useLoginMutation } from "../reduxStore/features/auth/authApi";
import Error from "../component/common/Error";

const Page = () => {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [error, setError] = useState("");

  const [login, { data, isLoading, error: responseError }] = useLoginMutation();
  const router = useRouter();
  console.log('login data', data)
  const handleSubmit = (e) => {
    e.preventDefault();
    console.log("loging data", email, password)
    setError("");

    login({
      email,
      password,
    });

    router.push("/");
  };

  useEffect(() => {
    if (responseError?.data) {
      setError(responseError.data);
    }
    if (data?.accessToken && data?.user) {
      router.push("/");
    }
  }, [data, responseError, router]);

  return (
    <div className="flex h-screen items-center justify-center bg-gray-100">
      <div className="bg-white p-8 shadow-md rounded-lg w-80">
        <h1 className="text-2xl font-semibold mb-4">Login </h1>
        <form onSubmit={handleSubmit}>
          <div className="mb-4">
            <label className="block text-sm font-medium text-gray-700">Email</label>
            <input
              type="email"
              className="mt-1 block w-full border rounded-md px-3 py-2 text-red-800"
              required
              value={email}
              onChange={(e) => setEmail(e.target.value)}
            />
          </div>
          <div className="mb-4">
            <label className="block text-sm font-medium text-gray-700">
              Password
            </label>
            <input
              type="password"
              className="mt-1 block w-full border rounded-md px-3 py-2 text-red-800"
              required
              value={password}
              onChange={(e) => setPassword(e.target.value)}
            />
          </div>
          <button
            type="submit"
            className="bg-blue-500 text-white py-2 px-4 rounded hover:bg-blue-600"
          >
            Login
          </button>
        </form>

        {error !== "" && <Error message={error} />}
      </div>
    </div>
  );
};

export default Page;

```
Hit “/login” URL show the Login page. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sfl27f1do1hxocpb7xni.png)

Now go to “/component/common/**LeftSideBar.js**. Here do this-

```
'use client';
import { AuthCheck } from '@/app/hooks/AuthCheck';
import { userLoggedOut } from '@/app/reduxStore/features/auth/authSlice';
import Link from 'next/link';
import { useRouter } from 'next/navigation';
import { useDispatch } from 'react-redux';
import ComingSoon from "../icons/ComingSoon";
import Community from "../icons/Community";
import Friends from "../icons/Friends";
import Home from "../icons/Home";
import Logout from "../icons/Logout";
import MakeMovie from "../icons/MakeMovie";
import Media from "../icons/Media";
import MovieLogo from "../icons/MovieLogo";
import Profile from "../icons/Profile";
import Settings from "../icons/Settings";

const LiftSideBar = () => {
  const isAuthenticated = AuthCheck();
  const dispatch = useDispatch();
  const router = useRouter();

  const logout = () => {
    dispatch(userLoggedOut());
    localStorage.clear();
    router.push("/login");
  };

  return (
    <aside className=" w-1/6 py-10 pl-10  min-w-min  border-r border-gray-300 dark:border-zinc-700  hidden md:block ">
      <Link href="/">
        <div className=" font-bold text-lg flex items-center gap-x-3">
          <MovieLogo />
          <div className="tracking-wide dark:text-white">
            RMovie<span className="text-red-600">.</span>
          </div>
        </div>
      </Link>

      <div className="mt-12 flex flex-col gap-y-4 text-gray-500 fill-gray-500 text-sm">
        <div className="text-gray-400/70  font-medium uppercase">Menu</div>
        <Link 
          href="/"
          className="flex items-center space-x-2 py-1 dark:text-white  font-semibold  border-r-4 border-r-red-600 pr-20 "
        >
          <Home />
          <span>Home</span>
        </Link>

        <Link 
          href="/ai-make-movie"
          className="flex items-center space-x-2 py-1  group hover:border-r-4 hover:border-r-red-600 hover:font-semibold dark:hover:text-white"
        >
         <MakeMovie />
          <span>Ai Make Movie</span>
        </Link>
        <Link 
          href="/manual-make-movie"
          className="flex items-center space-x-2 py-1  group hover:border-r-4 hover:border-r-red-600 hover:font-semibold dark:hover:text-white"
        >
         <MakeMovie />
          <span>Manual Make Movie</span>
        </Link>

        <a
          className=" flex items-center space-x-2 py-1  group hover:border-r-4 hover:border-r-red-600 hover:font-semibold dark:hover:text-white "
          href="#"
        >
          <Community />
          <span>Community</span>
        </a>
        <a
          className=" flex items-center space-x-2 py-1  group hover:border-r-4 hover:border-r-red-600 hover:font-semibold dark:hover:text-white "
          href="#"
        >
          <ComingSoon />
          <span>Coming Soon</span>
        </a>

        <div className="mt-8 text-gray-400/70  font-medium uppercase">
          Social
        </div>
        <a
          className=" flex items-center space-x-2 py-1  group hover:border-r-4 hover:border-r-red-600 hover:font-semibold dark:hover:text-white "
          href="#"
        >
          <Profile />
          <span>Profile</span>
        </a>
        <a
          className=" flex items-center space-x-2 py-1  group hover:border-r-4 hover:border-r-red-600 hover:font-semibold dark:hover:text-white "
          href="#"
        >
          <Friends />
          <span>Friends</span>
        </a>
        <Link
          className=" flex items-center space-x-2 py-1  group hover:border-r-4 hover:border-r-red-600 hover:font-semibold dark:hover:text-white "
          href="/make-category"
        >
          <Media />
          <span>Make Category</span>
        </Link>

        <div className="mt-8 text-gray-400/70  font-medium uppercase">
          General
        </div>
        <a
          className=" flex items-center space-x-2 py-1  group hover:border-r-4 hover:border-r-red-600 hover:font-semibold dark:hover:text-white "
          href="#"
        >
          <Settings />
          <span>Settings</span>
        </a>

        { isAuthenticated && (
        <a
          className=" flex items-center space-x-2 py-1  group hover:border-r-4 hover:border-r-red-600 hover:font-semibold dark:hover:text-white"
          href="#"
          onClick={logout}
        >
          <Logout />
          <span>Logout</span>
        </a>
         )}

        { !isAuthenticated && (
          <Link href="/login"
          className=" flex items-center space-x-2 py-1  group hover:border-r-4 hover:border-r-red-600 hover:font-semibold dark:hover:text-white"
        >
          <Logout />
          <span>login {AuthCheck}</span>
        </Link>
        )}
        
      </div>
    </aside>
  );
};

export default LiftSideBar;

```
Here we have Two action hook. 
   1. Login Button 
   2. Logout Button. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a4iyjk0clb1wupwkj68m.png)

**Step-3 : Category Api Setup**

Go to reduxStore/features/category folder and create this file
   1. categoryApi.js
   2. categorySlice.js

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/24vy3ivooc0niybo5lj7.png)

**categoryApi.js**

```
import { apiSlice } from "../api/apiSlice";

export const categoryApi = apiSlice.injectEndpoints({
    endpoints: (builder) => ({
        categoryWiseMovies: builder.query({
            query: () =>
                `/category-wise-movies`,
               
        }),
        allCategories: builder.query({
            query: () =>
                `/all-category`,
               
        }),
        categoryStore: builder.mutation({
            query: (data) => ({
                url: "/category-store",
                method: "POST",
                body: data,
            }),
        })
    }),
});

export const { useCategoryWiseMoviesQuery, useAllCategoriesQuery, useCategoryStoreMutation} = categoryApi;

```
Here we have Three end point. There are
   1. `/category-wise-movies`
   2. `/all-category`
   3. ` /category-store`

**categorySlice.js**

```
import { createSlice } from "@reduxjs/toolkit";

const initialState = {
    topMovies: [],
    
};

const categorySlice = createSlice({
    name: "movie",
    initialState,
    reducers: {
        topMovies: (state, action) => {
            state.topMovies = action.payload;
            
        }
       
    },
});

export const { topMovies} = categorySlice.actions;
export default categorySlice.reducer;

```
Here we have one action “topMovies”. 

**Step-4: Category Page Setup and Others**

Go to make-category folder and we can see page.js, now write this-

**page.js**

```
import LeftSideBar from "../component/common/LeftSideBar";
import RightSideBar from "../component/common/RightSideBar";
import AiFormInput from "../component/make-category/FormInput";
import ProtectedRoute from "../hooks/ProtectedRoute";

const Page = () => {
  return (
    <ProtectedRoute>
    <div className="flex min-h-screen">
      <LeftSideBar />
      <div className=" flex-1 py-10  px-5 sm:px-10 ">
        <AiFormInput />
      </div>
      <RightSideBar />
    </div>
    </ProtectedRoute>
  );
};

export default Page;

```
and go to component/make-category and see FormInput.js. Below this write-
**FormInput.js**

```
'use client';
import { useCategoryStoreMutation } from "@/app/reduxStore/features/category/categoryApi";
import Image from 'next/image';
import { useRouter } from 'next/navigation';
import { useState } from "react";

const FormInput = () => {
  const [categoryStore] = useCategoryStoreMutation();
  const router = useRouter();
  const [categoryData, setCategoryData] = useState({
    title: '',
    image: null,
  });

  const handleChange = (e) => {
    const { name, value } = e.target;
    setCategoryData({ ...categoryData, [name]: value });
  };

  const handleFileChange = (e) => {
    const file = e.target.files[0];
    setCategoryData({ ...categoryData, image: file });
  };

  const handleSubmit = async (e) => {
    e.preventDefault();

    const formData = new FormData();
    formData.append('name', categoryData.title);
    formData.append('image', categoryData.image);
    console.log('categry submit form', categoryData)

    try {
      const response = await categoryStore(formData);
      // Handle the success response
      console.log('Response from server:', response.data);
      // Assuming you want to reset the form after successful submission
      setCategoryData({
        name: '',
        image: null,
      });

      router.push("/");

    } catch (error) {
      console.error('Error saving the movie:', error);
    }
  };
  return (
    <div className="bg-gray-200 p-5 rounded-md dark:bg-black">
      <div className="text-center mb-8 relative">
        <h1 className="text-2xl font-bold text-gray-700 mb-3 dark:text-white">
          Create Category
        </h1>

        <Image
          src="/AS0007464_07.gif"
          alt="form"
          className="rounded-full  absolute -top-5 right-0 sm::hidden"
          width={105}
          height={105}

        />
      </div>

      <form onSubmit={handleSubmit}>
        <div className="mx-10 px-10 py-16 bg-gray-400 dark:bg-black dark:border rounded-md">
          <div className="mb-6">
            <label
              htmlFor="large-input"
              className="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
            >
              Category Name
            </label>
            <input
              type="text"
              id="large-input"
              className="block w-full p-4 text-gray-900 border border-gray-300 rounded-lg bg-gray-50 sm:text-md focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
              placeholder="Type your Category name..."
              name="title"
              onChange={handleChange}
            />
          </div>
          <div className="mb-6">
            <label
              htmlFor="large-input"
              className="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
            >
              Select Category Image
            </label>
            <input
              type="file"
              id="large-input"
              className="block w-full p-4 text-gray-900 border border-gray-300 rounded-lg bg-gray-50 sm:text-md focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
              name="image"
              onChange={handleFileChange}
            />
          </div>
          <div className="text-right">
            <button
              type="submit"
              className="bg-black text-white p-3 rounded-md dark:border"
            >
              Make Category
            </button>
          </div>
        </div>
      </form>

    </div>
  );
};

export default FormInput;

```
Now **“/make-category”** URL show this page and we can create category.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jnr0ic4kz4ascqckvrnz.png)

**Step-5: movie Api Setup**

Go to reduxStore/features/movie folder and create this file
   1. movieApi.js
   2. movieSlice.js


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mc8nldf927e2pnxy4eyb.png)

**movieApi.js**

```
import { apiSlice } from "../api/apiSlice";

export const moviesApi = apiSlice.injectEndpoints({
    endpoints: (builder) => ({
        topMovies: builder.query({
            query: () =>
                `/top-movies`,
        }),
        manualMovieStore: builder.mutation({
            query: (data) => ({
                url: "/movie-store",
                method: "POST",
                body: data,
            }),
        }),
        
        aiMovieStore: builder.mutation({
           
            query: (data) => ({
                
                url: "/ai-movie-store",
                method: "POST",
                body: data,
                
            }),
            async onQueryStarted(arg, { queryFulfilled, dispatch }) {
               console.log('redux', arg)
            },
        }),
        
    }),
});

export const { useTopMoviesQuery, useManualMovieStoreMutation, useAiMovieStoreMutation} = moviesApi;

```
Here we have Three end points. There are
   1. "/top-movies"
   2. "/movie-store"
   3. "/ai-movie-store"

**movieSlice.js**

```
import { createSlice } from "@reduxjs/toolkit";

const initialState = {
    topMovies: [],
    aiMovieData:{},
    sampleData:"sdfsf sdfsf"
    
};

const movieSlice = createSlice({
    name: "movie",
    initialState,
    reducers: {
        topMovies: (state, action) => {
            state.topMovies = action.payload;
            
        },
        aiMovieData:(state, action) =>{
            console.log('redux movie')
            state.aiMovieData = action.payload;
        }
       
    },
});

export const { topMovies, aiMovieData} = movieSlice.actions;
export default movieSlice.reducer;

```
Here we have Two actions. 
   1. topMovies
   2. aiMovieData

**Step-6: Manual Movie Page Setup and Others**

Go to manual-make-movie folder and see page.js file. Write this code-

```
import LeftSideBar from "../component/common/LeftSideBar";
import RightSideBar from "../component/common/RightSideBar";
import FormInput from "../component/manual-make-movie/FormInput";

const Page = () => {
  return (
    <div className="flex min-h-screen">
      <LeftSideBar />
      <div className=" flex-1 py-10  px-5 sm:px-10 ">
        <FormInput />
      </div>
      <RightSideBar />
    </div>
  );
};

export default Page;

```
Now go to component/manual-make-movie and write this
**FormInput.js**

```
'use client';
import { useAllCategoriesQuery } from "@/app/reduxStore/features/category/categoryApi";
import { useManualMovieStoreMutation } from "@/app/reduxStore/features/movie/moviesApi";
import Image from 'next/image';
import { useRouter } from 'next/navigation';
import { useState } from "react";

const FormInput = () => {
  const { data, isLoading, isError } = useAllCategoriesQuery();
  const [manualMovieStore, { isSuccess }] = useManualMovieStoreMutation();
  const router = useRouter();
  console.log("all manualMovieStore", manualMovieStore)

  const [movieData, setMovieData] = useState({
    title: '',
    category_id: '',
    description: '',
    image: null, 
  });

  const handleChange = (e) => {
    const { name, value } = e.target;
    setMovieData({ ...movieData, [name]: value });
  };

  const handleFileChange = (e) => {
    const file = e.target.files[0];
    setMovieData({ ...movieData, image: file });
  };

  const handleSubmit = async (e) => {
    e.preventDefault();

    const formData = new FormData();
    formData.append('title', movieData.title);
    formData.append('category_id', movieData.category_id);
    formData.append('description', movieData.description);
    formData.append('image', movieData.image);
    console.log('form submit', movieData)
    try {
      const response = await manualMovieStore(formData);
      // Handle the success response
      console.log('Response from server:', response.data);
      // Assuming you want to reset the form after successful submission
      setMovieData({
        title: '',
        category_id: '',
        description: '',
        image: null,
      });
      router.push("/");
    } catch (error) {
      // Handle the error
      console.error('Error saving the movie:', error);
    }
  };

  if (isLoading) {
    return <div>Loading...</div>;
  }

  if (isError) {
    return <div>Error fetching categories.</div>;
  }
  return (
    <div>
      <div className="text-center mb-8 relative">
        <h1 className="text-2xl font-bold text-gray-700 mb-3 dark:text-white">
          Manual Movie Maker
        </h1>
       
        <Image
                src="/storytelling-08.gif"
                alt="input"
                className="rounded-full w-48 h-48 absolute -top-5 right-0 sm::hidden"
                width={200}
                height={200}
            />
      </div>
      <form onSubmit={handleSubmit}>
        <div className="mx-10 px-10 py-16 bg-gray-400 dark:bg-black dark:border rounded-md">
          <div className="mb-6">
            <label
              htmlFor="large-input"
              className="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
            >
              Movie Title
            </label>
            <input
              type="text"
              id="large-input"
              className="block w-full p-4 text-gray-900 border border-gray-300 rounded-lg bg-gray-50 sm:text-md focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
              placeholder="Type your Movie Title..."
              name="title"
              onChange={handleChange}

            />
          </div>
          <div className="mb-6">
            <label
              htmlFor="base-input"
              className="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
            >
              Movie Category
            </label>
            <select
              name="category_id"
              onChange={handleChange}
              id="category"
              className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
            >
              <option value="">Choose category</option>
              {data && data.data?.map((category) => (
                <option key={category._id} value={category._id}>
                  {category.name}
                </option>
              ))}
            </select>
          </div>
          <div className="mb-6">
            <label
              htmlFor="message"
              className="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
            >
              Movie Short Description
            </label>
            <textarea
              id="message"
              rows={4}
              className="block p-2.5 w-full text-sm text-gray-900 bg-gray-50 rounded-lg border border-gray-300 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
              placeholder="Type Movie short description..."
              v-model="movieData.description"
              defaultValue={""}
              name="descriptin"
              onChange={handleChange}
            />
          </div>
          <div className="mb-6">
            <label
              htmlFor="large-input"
              className="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
            >
              Select Movie image
            </label>
            <input
              type="file"
              id="large-input"
              className="block w-full p-4 text-gray-900 border border-gray-300 rounded-lg bg-gray-50 sm:text-md focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
              placeholder="Type your Movie Title..."
              name="image"
              onChange={handleFileChange}
            />
          </div>
          <div className="text-right">
            <button
              type="submit"
              className="bg-black text-white p-3 rounded-md dark:border"
            >
              Make Movie
            </button>
          </div>
        </div>
      </form>

    </div>
  );
};

export default FormInput;

```
Now we are making manual movie form.  If you hit “/manual-make-movie” URL then you see this page.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j9o9sue9u0uk4s9yriid.png)

We can now create any movie using this form.

**Step-7 : Home Page setup**

Go to app folder and see page.js file. Write this code

```
import LeftSideBar from "./component/common/LeftSideBar";
import RightSideBar from "./component/common/RightSideBar";
import CategoryWiseMovies from "./component/index/CategoryWiseMovies";
import HeroSection from "./component/index/HeroSection";
import TopMovies from "./component/index/TopMovies";

export default function Home() {
  return (
    <div className="flex min-h-screen">
    <LeftSideBar />
    <div className=" flex-1 py-10  px-5 sm:px-10 ">
      <HeroSection />
      <TopMovies />
      <CategoryWiseMovies />
    </div>
    <RightSideBar />
    </div>
  )
}

```
Now Go to component/index Folder and see TipMovie.js file. Write below this code-

```
'use client';
import { useTopMoviesQuery } from "@/app/reduxStore/features/movie/moviesApi";
import Image from 'next/image';
import ImdbLogo from "../icons/ImdbLogo";
import NextPrev from "../icons/NextPrev";

const TopMovies = () => {
  const { data, error, isLoading } = useTopMoviesQuery();
  console.log("movie data", data)

  if (isLoading) {
      return <div>Loading...</div>;
  }

  if (error) {
    return <div>Error: {error.message}</div>;
  }
  
  const getRandomNumber = () => {
    return Math.floor(Math.random() * 10) + 1;
  };

  return (
    <section className="mt-9 bg-lime-300 dark:bg-rose-500 p-5 rounded-md">
      <div className="flex items-center justify-between">
        <span className="font-semibold text-gray-700 text-base dark:text-white">
          My Favourite Movies and Collections
        </span>
        <div className="flex items-center space-x-2 fill-gray-500">
          <NextPrev />
        </div>
      </div>

      <div className="mt-4 grid grid-cols-2  sm:grid-cols-4 gap-x-5 gap-y-5">
        {data.data && data.data.map((movie, index) => (
          <div
            key={index}
            className="flex flex-col rounded-xl overflow-hidden aspect-square border dark:border-zinc-600"
          >
            
            
            <div className="w-full h-1/5 bg-white dark:bg-zinc-800 dark:text-white px-3 flex items-center justify-between border-t-2 border-t-red-600">
              <span className="capitalize font-medium truncate">{movie.title}</span>
              <div className="flex space-x-2 items-center text-xs">
                <ImdbLogo />
                <span>{getRandomNumber()}</span>
              </div>
            </div>
            <Image
                src={movie.image}
                className="h-4/5 object-cover w-full"
                alt={movie.title}
                width={105} 
                height={105} 
                layout="responsive"
            />
          </div>
        ))}
      </div>
      {isLoading && isLoading}
      {error && error}
    </section>
  );
};

export default TopMovies;

```
Now same Folder we can see CategoryWiseMovies.js file. Write this code-

```
/* eslint-disable react/jsx-key */
'use client';
import { useCategoryWiseMoviesQuery } from "@/app/reduxStore/features/category/categoryApi";
import Image from 'next/image';
import ImdbLogo from "../icons/ImdbLogo";
import NextPrev from "../icons/NextPrev";

const CategoryWiseMovies = () => {
  const { data, error, isLoading } = useCategoryWiseMoviesQuery();
  console.log('category wise movie data', data)

  const getRandomColor = () => {
    const colors = ['#FF5733', '#33FF57', '#5733FF', '#33FFFF', '#FF33FF', '#FFFF33'];
    return colors[Math.floor(Math.random() * colors.length)];
  };
  const getRandomNumber = () => {
    return Math.floor(Math.random() * 10) + 1;
  };
  if (isLoading) {
    return <div>Loading...</div>;
  }

  if (error) {
    return <div>Error: {error.message}</div>;
  }

  return (
    <>
      {data.data.map((category, indexOne) => (
        <section className="mt-9 bg-blue-400 dark:bg-black p-5 rounded-md" style={{ backgroundColor: getRandomColor() }} key={indexOne}>
          <div className="flex items-center justify-between">
            <span className="font-semibold text-gray-700 text-base dark:text-white">{category.name} Movies</span>
            <div className="flex items-center space-x-2 fill-gray-500">
              <NextPrev />
            </div>
          </div>

          <div className="mt-4 grid grid-cols-2 gap-y-5 sm:grid-cols-3 gap-x-5 ">
            {category.movies.map((movie, indexTwo) => (
              <div className="flex flex-col rounded-xl overflow-hidden aspect-square border dark:border-zinc-600" key={indexTwo}>
                <div className="w-full h-1/5 bg-white dark:bg-zinc-800 dark:text-white px-3 flex items-center justify-between border-t-2 border-t-red-600">
                  <span className="capitalize  font-medium truncate">{movie.title}</span>
                  <div className="flex space-x-2 items-center text-xs">
                    <ImdbLogo />
                    <span>{getRandomNumber()}</span>
                  </div>
                </div>
                <Image
                  src={movie.image}
                  className=" h-4/5 object-cover w-full"
                  alt={movie.title}
                  width={105} 
                height={105} 
                layout="responsive"
                />
              </div>
            ))}

          </div>

        </section>
      ))}
    </>
  );
};

export default CategoryWiseMovies;

```
Now ready home page. If we are hit http://localhost:3000/ then we can see our home page. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ap8an48x85e20goa0q8z.png)

If your database is empty then you don't see any movie  😊

Next we set up AI Prompt. Here is this Tutorial-

AI Prompt setup on Nextjs. [Link](https://dev.to/kamruzzaman/ai-prompt-setup-on-nextjs-2109)

full Project github
**Node**
https://github.com/kamruzzamanripon/node-movie-api
**NextJs**
https://github.com/kamruzzamanripon/next-movie-ui-with-node-api
**NextJs UI**
https://github.com/kamruzzamanripon/next-movie-ui

That's all. Happy Learning :) .
[if it is helpful, giving a star to the repository 😇]