AI Prompt setup on Nextjs.

syed kamruzzaman
14 min readDec 17, 2023

--

Photo by Matt Wang on Unsplash

In this tutorial, we have setup our AI Prompt. Previously we did 3 parts. If you don’t know then see this link

  1. Making API Using Node and Express. Link
  2. Scaffolding redux-toolkit in NextJs. Link
  3. API Setup in NextJs. Link

Now we are doing 4th part. Let’s derive on-

Step-1 : .env file Setup and necessary package install

In your root project create .env.local file and do this

Here API key is a AI key which you get form your Ai Dashboard. https://platform.openai.com/

If you don’t have any AI API key then crate one and put in you .env.local file. And then install this package

npm i openai@3.3.0

Step-2 : movie Api Setup

Previously we did a movie Api setup. API Setup in NextJs.
Go to reduxStore/features/movie folder and create this file

  1. movieApi.js
  2. movieSlice.js

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 Now, movieApi.js file, we have aiMovieStore method for store Ai information in our Database. Here you saw aiMovieData action method. This action store AI information in redux store.

Step-3: Ai Movie Page Setup and Others
Go to ai-make-movie folder and see page.js file. Write this code-

import AiFormInput from "../component/ai-make-movie/AiFormInput";
import LeftSideBar from "../component/common/LeftSideBar";
import RightSideBar from "../component/common/RightSideBar";

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

export default Page;

Now go to component/ai-make-movie and you see AiFormInput.js file

AiFormInput.js

'use client';
import { aiMovieData } from '@/app/reduxStore/features/movie/movieSlice';
import Image from 'next/image';
import { useRouter } from 'next/navigation';
import { Configuration, OpenAIApi } from 'openai';
import { useState } from "react";
import { useDispatch } from 'react-redux';
import Loading from '../../ai-make-movie/loading';

const AiFormInput = () => {
const [aiIdea, setAiIdea] = useState();
const dispatch = useDispatch();
const router = useRouter();
const [loading, setLoading] = useState(false);

//Ai configuration
const openAiApiKey = process.env.NEXT_PUBLIC_OPEN_AI_API_KEY;
const configuration = new Configuration({
apiKey: openAiApiKey
})
const openAi = new OpenAIApi(configuration);

//Ai form submit handle
const handleSubmit = async (e) => {
e.preventDefault();
setLoading(true);
const aiSampleMovieData = await fetchSynopsis(aiIdea)
dispatch(aiMovieData(aiSampleMovieData));
//console.log('form submit', data)

router.push("/movie-made")

};

async function fetchSynopsis(outline){
const response = await openAi.createCompletion({
model: 'text-davinci-003',
prompt:`Generate an engaging, professional and marketable movie synopsis based on an outline. The synopsis should include actiors names in brackets after each character. Choose actors that would be ideal for this role.
###
outline: A big-headed daredevil fighter pilot goes back to school only to be sent on a deadly mission.
synopsis: The Top Gun Naval Fighter Weapons School is where the best of the best train to refine their elite flying skills, When hotshot fighter pilot Maverick (Tom Cruise) is sent o the school, his rackless attitude and cocky demeanor put him at odds with the other pilots, especially the cool and collected Iceman (Val kilmer). But Maverick ins't only competing to be the top fighter pilot, he's also fighting for the attention of his beautiful flight instructor, Charlotte Blackwood (kelly McGillis). Maverick gradually earns the respect of his intructors and peers - and also the love of Charlotte, but struggles to balance his personal and professional life. As the pilots prepare for a mission against a foreign enemy, Maverick must confront his own demons and overcome the tragedies rooted deep in his past to become the best fighter pilot and return from the mission triumphant.
###
outline: ${outline}
synopsis:
`,
max_tokens: 700
})
const synopsis = response.data.choices[0].text.trim();
console.log('fetchSynopsis', synopsis)
const aiMovieData = fetchTitle(synopsis);
return aiMovieData;

}

async function fetchTitle(synopsis){
const response = await openAi.createCompletion({
model: 'text-davinci-003',
prompt:`Generate a catchy movie title for this synopsis: ${synopsis}`,
max_tokens: 25,
temperature: 0.7
})
const title = response.data.choices[0].text.trim();
console.log('fetchTitle-1', synopsis)
console.log('fetchTitle-2', title)
const aiMovieData = fetchImagePromt(title, synopsis)
return aiMovieData;
}

async function fetchImagePromt(title, synopsis){
const response = await openAi.createCompletion({
model: 'text-davinci-003',
prompt: `Give a short description of an image which could be used to advertise a movie based on a title and synopsis.The description should be rich in visual detail but contain no names.
###
title: Love's Time Warp
synopsis: When scientist and time traveller Wendy (Emma Watson) is sent back to the 1920s to assassinate a future dictator, she never expected to fall in love with them. As wendy infiltrates the dictator's inner circle, she soon finds herself torn between her mission and her growing feelings for the leader (Brie Larson). With the help of a mysterious stranger from the future (Josh Brolin), Wendy must decide whether to carry out her mission or follow her heart. But the choices she makes in the 1920s will have far-reaching consequences that reverberate throuugh the ages.
image description: A silhouetted figure stands in the shadows of a 1920s speakeasy, her face turned away from the camera. In the background, two people are dancing in the dim light, one wearing a flapper-style dress and the other wearing a dapper suit. A semi-transparent image of war is super-imposed over the scene.
###
title: zero Earth
synopsis: When baodyguard kob (Daniel Radcliffe) is recruited by the United Nations to save Planet Earth from the sinister Simm (John Malkovich), an alien lord with a plan to take over the world, he reluctantly accepts the challenge, With the help of this loyal sidekick, a brave and resourceful hamster named Gizmo (Gaten Matarazzo), Kob embarks on a perilous mission to destroy Simm. Along the way, he discovers a newfound courage and strength as he battles Simms's merciless forces. With the fate of the world in his hands, kob must find a way to defeat the alien lord and save the planet.
image description: A tired and bloodied bodyguard and hamster standing a top a tall skyscraper, looking out over a vibrant cityscape, with a rainbow in the sky above them.
###
title: ${title}
synopsis: ${synopsis}
image description:
`,
temperature: 0.8,
max_tokens: 100

})
console.log('fetchImagePromt-1', synopsis)
console.log('fetchImagePromt-2', title)
const aiMovieData = fetchImageUrl(response.data.choices[0].text.trim(), synopsis, title)
return aiMovieData;
}

async function fetchImageUrl(imagePrompt,synopsis,title){
const response = await openAi.createImage({
prompt: `${imagePrompt}. There should be no text in this iamge.`,
n: 1,
size: '256x256',
response_format: 'b64_json'
})
const b64JsonImage = response.data.data[0];
console.log('fetchImageUrl-1', synopsis)
console.log('fetchImageUrl-2', title)
const aiMovieData ={
b64JsonImage,
synopsis,
title
}

return aiMovieData;
}

if(loading){
return <Loading/>
}

return (
<div>
<form onSubmit={handleSubmit}>
<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">
Hello, I am here to help Make Movie
</h1>
<p className="dark:text-white">
Please, write your idea below input box
</p>

<Image
src="/robot-1.webp"
alt="Bird flying animation"
className="rounded-full w-48 h-48 absolute top-0 right-0"
width={105}
height={105}

/>
</div>

<br /><br /><br /><br />
<div className="mb-6">
<label
htmlFor="message"
className="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
>
Type your Idea
</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 Idea..."
onChange={(e)=>setAiIdea(e.target.value)}

/>

</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 AiFormInput;

Here we are implement AI Prompt. Let’s do it-
If you hit this http://localhost:3000/ai-make-movie URL then you can see this page-

This page has one input field. User just type his idea and then click Make Movie button then AI generate our information. Let’s drive in our code. Here you see handleSubmit method.

//Ai form submit handle
const handleSubmit = async (e) => {
e.preventDefault();
setLoading(true);
const aiSampleMovieData = await fetchSynopsis(aiIdea)
dispatch(aiMovieData(aiSampleMovieData));
//console.log('form submit', data)

router.push("/movie-made")

};

This method Tigger when we click Make Movie button. This method first call fetchSynopsis method and passed user input idea.

async function fetchSynopsis(outline){
const response = await openAi.createCompletion({
model: 'text-davinci-003',
prompt:`Generate an engaging, professional and marketable movie synopsis based on an outline. The synopsis should include actiors names in brackets after each character. Choose actors that would be ideal for this role.
###
outline: A big-headed daredevil fighter pilot goes back to school only to be sent on a deadly mission.
synopsis: The Top Gun Naval Fighter Weapons School is where the best of the best train to refine their elite flying skills, When hotshot fighter pilot Maverick (Tom Cruise) is sent o the school, his rackless attitude and cocky demeanor put him at odds with the other pilots, especially the cool and collected Iceman (Val kilmer). But Maverick ins't only competing to be the top fighter pilot, he's also fighting for the attention of his beautiful flight instructor, Charlotte Blackwood (kelly McGillis). Maverick gradually earns the respect of his intructors and peers - and also the love of Charlotte, but struggles to balance his personal and professional life. As the pilots prepare for a mission against a foreign enemy, Maverick must confront his own demons and overcome the tragedies rooted deep in his past to become the best fighter pilot and return from the mission triumphant.
###
outline: ${outline}
synopsis:
`,
max_tokens: 700
})
const synopsis = response.data.choices[0].text.trim();
console.log('fetchSynopsis', synopsis)
const aiMovieData = fetchTitle(synopsis);
return aiMovieData;

}

This fetchSynopsis method has dummy result, so that AI know what I need actually. Then I passed outline which I passed as aiIdea Parameter. So this method give me some result. Then I sort this result and get my Synopsis or Description. Then I call another method fetchTitle for get a catchy Title. Here I passed my synopsis result as a Parameter.

async function fetchTitle(synopsis){
const response = await openAi.createCompletion({
model: 'text-davinci-003',
prompt:`Generate a catchy movie title for this synopsis: ${synopsis}`,
max_tokens: 25,
temperature: 0.7
})
const title = response.data.choices[0].text.trim();
console.log('fetchTitle-1', synopsis)
console.log('fetchTitle-2', title)
const aiMovieData = fetchImagePromt(title, synopsis)
return aiMovieData;
}

Now this method give me Movie Title require as my synopsis. Then again I call another method fetchImagePromt for image description. Here I passed Two parameter — title and synopsis

async function fetchImagePromt(title, synopsis){
const response = await openAi.createCompletion({
model: 'text-davinci-003',
prompt: `Give a short description of an image which could be used to advertise a movie based on a title and synopsis.The description should be rich in visual detail but contain no names.
###
title: Love's Time Warp
synopsis: When scientist and time traveller Wendy (Emma Watson) is sent back to the 1920s to assassinate a future dictator, she never expected to fall in love with them. As wendy infiltrates the dictator's inner circle, she soon finds herself torn between her mission and her growing feelings for the leader (Brie Larson). With the help of a mysterious stranger from the future (Josh Brolin), Wendy must decide whether to carry out her mission or follow her heart. But the choices she makes in the 1920s will have far-reaching consequences that reverberate throuugh the ages.
image description: A silhouetted figure stands in the shadows of a 1920s speakeasy, her face turned away from the camera. In the background, two people are dancing in the dim light, one wearing a flapper-style dress and the other wearing a dapper suit. A semi-transparent image of war is super-imposed over the scene.
###
title: zero Earth
synopsis: When baodyguard kob (Daniel Radcliffe) is recruited by the United Nations to save Planet Earth from the sinister Simm (John Malkovich), an alien lord with a plan to take over the world, he reluctantly accepts the challenge, With the help of this loyal sidekick, a brave and resourceful hamster named Gizmo (Gaten Matarazzo), Kob embarks on a perilous mission to destroy Simm. Along the way, he discovers a newfound courage and strength as he battles Simms's merciless forces. With the fate of the world in his hands, kob must find a way to defeat the alien lord and save the planet.
image description: A tired and bloodied bodyguard and hamster standing a top a tall skyscraper, looking out over a vibrant cityscape, with a rainbow in the sky above them.
###
title: ${title}
synopsis: ${synopsis}
image description:
`,
temperature: 0.8,
max_tokens: 100

})
console.log('fetchImagePromt-1', synopsis)
console.log('fetchImagePromt-2', title)
const aiMovieData = fetchImageUrl(response.data.choices[0].text.trim(), synopsis, title)
return aiMovieData;
}

Here inside this method, first I setup dummy result then I setup my parameter. This method give me image description as require title and synopsis. Now I get Three result

  1. synopsis or Description
  2. title or Movie Title
  3. imagePrompt or Image Description. Now we call, another method fetchImageUrl for image create.
async function fetchImageUrl(imagePrompt,synopsis,title){
const response = await openAi.createImage({
prompt: `${imagePrompt}. There should be no text in this iamge.`,
n: 1,
size: '256x256',
response_format: 'b64_json'
})
const b64JsonImage = response.data.data[0];
console.log('fetchImageUrl-1', synopsis)
console.log('fetchImageUrl-2', title)
const aiMovieData ={
b64JsonImage,
synopsis,
title
}

return aiMovieData;
}

This method give me Base64 image and size 256 x 256. We can increase size. Now we get three information from AI and return this information like as object to handleSubmit method

//Ai form submit handle
const handleSubmit = async (e) => {
e.preventDefault();
setLoading(true);
const aiSampleMovieData = await fetchSynopsis(aiIdea)
dispatch(aiMovieData(aiSampleMovieData));
//console.log('form submit', data)

router.push("/movie-made")

};

Here this method got all information from AI and then dispatch this information to aiMovieData action, which we were create movieSlice.js

So, aiMovieDate method store AI information.

Now we show this information in our redux extension plugin.

Step-4: Ai Result Show and Others

Go to /movie-made folder, open page.js and do this

import AiFormResult from "../component/ai-make-movie/AiFormResult";
import LeftSideBar from "../component/common/LeftSideBar";
import RightSideBar from "../component/common/RightSideBar";

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

export default Page;

Now go to /component/ai-make-movie folder and open AiFormResult.js and write this-

'use client';
import { useAllCategoriesQuery } from "@/app/reduxStore/features/category/categoryApi";
import { useAiMovieStoreMutation } from "@/app/reduxStore/features/movie/moviesApi";
import Image from 'next/image';
import { useRouter } from "next/navigation";
import { useEffect, useState } from "react";
import { useSelector } from "react-redux";

const AiFormResult = () => {
const [movieData, setMovieData] = useState({
title: '',
category_id: '',
description: '',
image: null,
});
const aiMovieData = useSelector((state)=>state.movies.aiMovieData);
const { data, isLoading, isError } = useAllCategoriesQuery();
const router = useRouter()
const [aiMovieStore, { isSuccess }] = useAiMovieStoreMutation();

//b64_json convert image src
const getImageSource = () => {
const imageData = aiMovieData?.b64JsonImage?.b64_json || '';
return `data:image/png;base64,${imageData}`;
};


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

//set ai data into movieData
useEffect(()=>{
setMovieData({
title: aiMovieData?.title,
description: aiMovieData?.synopsis,
image: aiMovieData?.b64JsonImage?.b64_json,
});
},[aiMovieData?.b64JsonImage?.b64_json, aiMovieData?.synopsis, aiMovieData?.title])

const handleSubmit = async (e) => {
e.preventDefault();
if(movieData.category_id == ''){
alert('please Select Category')
return;
}

console.log('Response from server:-2', movieData);

try {
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)
const response = await aiMovieStore(formData);
// Handle the success response
console.log('Response from server:', [...formData]);
// 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);
}
};


return (
<div className="bg-gray-200 p-5 rounded-md dark:bg-black my-10">
<div className="text-center mb-8">
<h1 className="text-2xl font-bold text-gray-700 mb-3 dark:text-white">
This is your Search Result
</h1>
</div>
<form onSubmit={handleSubmit}>
<div className="mx-10 px-10 py-16 bg-gray-400 dark:bg-black dark:border">
<div className="mb-6 flex items-center justify-center">

<Image
src={getImageSource()}
alt="result-image"
className="rounded-md border border-2 border-x-1 w-[350px]"
name="image"
onChange={handleChange}
width={105}
height={105}
layout="responsive"
/>
</div>


<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..."
value={aiMovieData?.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
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"
name="category_id"
onChange={handleChange}
>
<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={8}
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..."
value={aiMovieData?.synopsis}
name="description"
onChange={handleChange}
/>
</div>
<div className="text-right">
<button className="bg-black text-white p-3 rounded-md dark:border">
Save your Movie
</button>
</div>
</div>
</form>

</div>
);
};

export default AiFormResult;

now hit http://localhost:3000/movie-made URL then you can see this page

Here This page, Show AI information. If you click Save your Movie button then it Tigger aiMovieStore
Method which we were define in moviesApi.js file. This method save Ai information data in our MongoDB.

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;

Now we Finished our AI Prompt.

Form Page
loading page
AI Information show
Home page

Next we deploy this App. Here is this Tutorial-

Deploy A NextJs App and Node API on a Linux Server. 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 😇]

--

--

syed kamruzzaman

I'm a Frontend and Backend developer with a passion for PHP and JavaScript, along with expertise in JavaScript-related libraries.