DEV Community

Cover image for API Setup in NextJs
syed kamruzzaman
syed kamruzzaman

Posted on

API Setup in NextJs

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
  2. Scaffolding redux-toolkit in NextJs. Link

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

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;

Enter fullscreen mode Exit fullscreen mode

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;

Enter fullscreen mode Exit fullscreen mode

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;

Enter fullscreen mode Exit fullscreen mode

Hit “/login” URL show the Login page.

Image description

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;

Enter fullscreen mode Exit fullscreen mode

Here we have Two action hook.

  1. Login Button
  2. Logout Button.

Image description

Step-3 : Category Api Setup

Go to reduxStore/features/category folder and create this file

  1. categoryApi.js
  2. categorySlice.js

Image description

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;

Enter fullscreen mode Exit fullscreen mode

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;

Enter fullscreen mode Exit fullscreen mode

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;

Enter fullscreen mode Exit fullscreen mode

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;

Enter fullscreen mode Exit fullscreen mode

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

Image description

Step-5: movie Api Setup

Go to reduxStore/features/movie folder and create this file

  1. movieApi.js
  2. movieSlice.js

Image description

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;

Enter fullscreen mode Exit fullscreen mode

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;

Enter fullscreen mode Exit fullscreen mode

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;

Enter fullscreen mode Exit fullscreen mode

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;

Enter fullscreen mode Exit fullscreen mode

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

Image description

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>
  )
}

Enter fullscreen mode Exit fullscreen mode

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;

Enter fullscreen mode Exit fullscreen mode

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;

Enter fullscreen mode Exit fullscreen mode

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

Image description

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

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 😇]

Top comments (0)