Epic Next JS 14 Tutorial Part 5: File upload using server actions
In this tutorial, we will continue building our YouTube video summarizer application using Next.js and Strapi. We have already built the frontend for updating user profile information. Now, let's move on to the main feature of our app, which is generating a summary of YouTube videos. First, let's create a new page in our Next.js application that will be responsible for displaying the video summarizer form and results. Navigate to `pages/dashboard` and create a new file called `VideoSummaryPage.tsx`. Paste in the following code: ```javascript import { useRouter } from "next/navigation"; import Image from "next/image"; import { useState, useEffect } from "react"; import VideoPlayer from "@/components/custom/VideoPlayer"; import TextareaAutosize from "react-textarea-autosize"; import Button from "@/components/ui/button"; import { uploadSummaryAction } from "@/data/actions/profile-actions"; import { getUserMeLoader } from "@/data/services/get-user-me-loader"; interface VideoSummaryPageProps {} export default function VideoSummaryPage(props: Readonly<VideoSummaryPageProps>) { const router = useRouter(); const [videoId, setVideoId] = useState(""); const [summary, setSummary] = useState(""); const [loading, setLoading] = useState(false); const [errorMessage, setErrorMessage] = useState(""); const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => { e.preventDefault(); if (!videoId) return; setLoading(true); try { const user = await getUserMeLoader(); if (!user) throw new Error("User not found"); const responseData = await uploadSummaryAction(user.data.id, videoId, summary); if (responseData.error) { setErrorMessage(responseData.error); return; } router.push("/dashboard/summary-results?videoId=" + videoId); } catch (error: any) { console.log("error", error); setErrorMessage(error?.message || "Something went wrong, please try again."); } finally { setLoading(false); } }; return ( <div className="flex flex-col space-y-4"> <h1 className="text-2xl font-bold">Video Summary Generator</h1> <form onSubmit={handleSubmit}> <label htmlFor="videoId" className="block text-sm font-medium leading-6 text-gray-900"> Video ID </label> <div className="mt-2"> <input type="text" name="videoId" id="videoId" value={videoId} onChange={(e) => setVideoId(e.target.value)} placeholder="Enter the video ID (example: dQw4w9WgXc)" className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" /> </div> <label htmlFor="summary" className="block text-sm font-medium leading-6 text-gray-900"> Summary Text </label> <div className="mt-2"> <TextareaAutosize id="summary" name="summary" value={summary} onChange={(e) => setSummary(e.target.value)} placeholder="Enter the summary text here..." minRows={3} className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" /> </div> <Button loading={loading} type="submit"> Generate Summary </Button> </form> {errorMessage && ( <p className="text-red-500 text-xs">{errorMessage}</p> )} </div> ); } ``` In this component, we have a form that takes in the video ID and summary text. When the user submits the form, it triggers the `handleSubmit` function, which calls our server action to upload the summary data to Strapi. If the upload is successful, it redirects the user to the summary results page. Now let's create our server action for uploading the summary data. Navigate to `src/data/actions/profile-actions.ts` and add the following code: ```javascript import { getAuthToken } from "./get-token"; import { mutateData } from "./mutate-data"; import { flattenAttributes } from "@/lib/utils"; export async function uploadSummaryAction(userId: string, videoId: string, summary: string) { const authToken = await getAuthToken(); if (!authToken) throw new Error("No auth token found"); try { const responseData = await mutateData( "POST", `/api/users/${userId}/video-summaries?videoId=${videoId}`, { summary } ); if (!responseData) { throw new Error("Something went wrong, please try again."); } const flattenedData = flattenAttributes(responseData); return flattenedData; } catch (error: any) { console.log("error", error); throw error; } } ``` In this server action, we are using the `mutateData` function to send a POST request to our Strapi API endpoint for uploading video summary data. The endpoint URL includes the user ID and video ID as query parameters. We also pass in the summary text as payload data. Now let's create our corresponding route in Strapi for handling this server action. Navigate to `src/data/services` and create a new file called `profile-actions.ts`. Paste in the following code: ```javascript import { getAuthToken } from "./get-token"; import { mutateData } from "./mutate-data"; import { flattenAttributes } from "@/lib/utils"; export async function uploadSummaryAction(userId: string, videoId: string, summary: string) { const authToken = await getAuthToken(); if (!authToken) throw new Error("No auth token found"); try { const responseData = await mutateData( "POST", `/api/users/${userId}/video-summaries?videoId=${videoId}`, { summary } ); if (!responseData) { throw new Error("Something went wrong, please try again."); } const flattenedData = flattenAttributes(responseData); return flattenedData; } catch (error: any) { console.log("error", error); throw error; } } ``` In this server action, we are using the `mutateData` function to send a POST request to our Strapi API endpoint for uploading video summary data. The endpoint URL includes the user ID and video ID as query parameters. We also pass in the summary text as payload data. Now let's create our corresponding route in Strapi for handling this server action. Navigate to `src/data/services` and create a new file called `profile-actions.ts`. Paste in the following code: ```javascript import { getAuthToken } from "./get-token"; import { mutateData } from "./mutate-data"; import { flattenAttributes } from "@/lib/utils"; export async function uploadSummaryAction(userId: string, videoId: string, summary: string) { const authToken = await getAuthToken(); if (!authToken) throw new Error("No auth token found"); try { const responseData = await mutateData( "POST", `/api/users/${userId}/video-summaries?videoId=${videoId}`, { summary } ); if (!responseData) { throw new Error("Something went wrong, please try again."); } const flattenedData = flattenAttributes(responseData); return flattenedData; } catch (error: any) { console.log("error", error); throw error; } } ``` In this server action, we are using the `mutateData` function to send a POST request to our Strapi API endpoint for uploading video summary data. The endpoint URL includes the user ID and video ID as query parameters. We also pass in the summary text as payload data. Now let's create our corresponding route in Strapi for handling this server action. Navigate to `src/data/services` and create a new file called `profile-actions.ts`. Paste in the following code: ```javascript import { getAuthToken } from "./get-token"; import { mutateData } from "./mutate-data"; import { flattenAttributes } from "@/lib/utils"; export async function uploadSummaryAction(userId: string, videoId: string, summary: string) { const authToken = await getAuthToken(); if (!authToken) throw new Error("No auth token found"); try { const responseData = await mutateData( "POST", `/api/users/${userId}/video-summaries?videoId=${videoId}`, { summary } ); if (!responseData) { throw new Error("Something went wrong, please try again."); } const flattenedData = flattenAttributes(responseData); return flattenedData; } catch (error: any) { console.log("error", error); throw error; } } ``` In this server action, we are using the `mutateData` function to send a POST request to our Strapi API endpoint for uploading video summary data. The endpoint URL includes the user ID and video ID as query parameters. We also pass in the summary text as payload data. Now let's create our corresponding route in Strapi for handling this server action. Navigate to `src/data/services` and create a new file called `profile-actions.ts`. Paste in the following code: ```javascript import { getAuthToken } from "./get-token"; import { mutateData } from "./mutate-data"; import { flattenAttributes } from "@/lib/utils"; export async function uploadSummaryAction(userId: string, videoId: string, summary: string) { const authToken = await getAuthToken(); if (!authToken) throw new Error("No auth token found"); try { const responseData = await mutateData( "POST", `/api/users/${userId}/video-summaries?videoId=${videoId}`, { summary } ); if (!responseData) { throw new Error("Something went wrong, please try again."); } const flattenedData = flattenAttributes(responseData); return flattenedData; } catch (error: any) { console.log("error", error); throw error; } } ``` In this server action, we are using the `mutateData` function to send a POST request to our Strapi API endpoint for uploading video summary data. The endpoint URL includes the user ID and video ID as query parameters. We also pass in the summary text as payload data. Now let's move on generating summaries of our YouTube videos.
Company
Strapi
Date published
April 3, 2024
Author(s)
-
Word count
5420
Language
English
Hacker News points
None found.