Summary of the program: I am trying to upload an image to Next.js server-less backend. I use formData() to append/set the data and POST it using axios to the backend. In the backend, I am requesting the formData() from NextRequest, changing it into buffer and saving it in the server file system.
Problem:
Whenever I fire-up the publish on the front-end I can see error on my Next.js console saying TypeError: s is not a function
.
Full error:
TypeError: s is not a function
at PATH\node_modules\next\dist\compiled\next-server\app-page.runtime.dev.js:37:5830
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async tX (PATH\node_modules\next\dist\compiled\next-server\app-page.runtime.dev.js:37:5207)
at async rl (PATH\node_modules\next\dist\compiled\next-server\app-page.runtime.dev.js:38:22994)
at async doRender (PATH\node_modules\next\dist\server\base-server.js:1407:30)
at async cacheEntry.responseCache.get.routeKind (PATH\node_modules\next\dist\server\base-server.js:1571:28)
at async DevServer.renderToResponseWithComponentsImpl (PATH\node_modules\next\dist\server\base-server.js:1479:28)
at async DevServer.renderErrorToResponseImpl (PATH\node_modules\next\dist\server\base-server.js:2104:24)
at async pipe.req.req (PATH\node_modules\next\dist\server\base-server.js:1982:30)
at async DevServer.pipeImpl (PATH\node_modules\next\dist\server\base-server.js:902:25)
Client (page.tsx):
"use client";
import React from "react";
import dynamic from "next/dynamic";
import "react-quill/dist/quill.snow.css";
import { useRouter } from "next/navigation";
import axios from "axios";
// Dynamically import ReactQuill to ensure it's only loaded on the client side
const ReactQuill = dynamic(() => import("react-quill"), { ssr: false });
var toolbarOptions = [
// toolbar options
];
const BLOG_API = "/api/blogs";
export default function WriteBlogPage() {
const router = useRouter();
const [title, setTitle] = React.useState("");
const [image, setImage] = React.useState<File>();
const [content, setContent] = React.useState("");
const [slug, setSlug] = React.useState("");
const handleContentChange = (value: any) => {
setContent(value);
};
const handleFormSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setSlug(title.replaceAll(" ", "-").toLowerCase());
if (!image || !title || !content || !slug) return;
try {
const data = new FormData();
data.append("image", image, image.name);
data.append("title", title);
data.append("content", content);
data.append("slug", slug);
const response = await axios.post(BLOG_API, data);
if (response.status === 200) {
router.push("/");
} else {
console.error("Error posting the blog");
}
} catch (error) {
console.error("Error posting the blog:", error);
}
};
return (
<form onSubmit={handleFormSubmit}>
<div className="w-full">
{
<ReactQuill
value={content}
onChange={handleContentChange}
modules={{
toolbar: toolbarOptions,
}}
className="text-center"
placeholder="Write an epic"
/>
}
</div>
<input
type="file"
accept="image/*"
onChange={(e) => setImage(e.target.files?.[0])}
/>
<input
type="text"
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
<button
type="submit">
Publish
</button>
</form>
);
}
Server (route.ts):
// Import necessary modules and models
import { NextRequest, NextResponse } from "next/server";
import { dbConnect as connect } from "@/dbConfig/dbConfig";
import Blog from "@/models/blogModel";
import NodeCache from "node-cache";
import path, { join } from "path";
import { writeFile } from "fs";
// Connect to the database
connect();
// NodeCache configuration for caching
const cache = new NodeCache({
stdTTL: 1800,
checkperiod: 300,
});
export const config = {
api: {
bodyParser: false,
},
};
// POST route for creating a new blog
export async function POST(req: NextRequest, res: NextResponse) {
try {
const data = await req.formData();
const image: File | null = data.get("image") as unknown as File;
const title: String | null = data.get("title") as unknown as String;
const content: String | null = data.get("content") as unknown as String;
const slug: String | null = data.get("slug") as unknown as String;
if (!image || !title || !content || !slug) {
return NextResponse.json({
success: false,
message: "Please pass all necessary data",
});
}
const blogTitle = await Blog.findOne({ title });
if (blogTitle) {
return NextResponse.json({
success: false,
message:
"Already posted. More than one blog of the same title is not allowed",
});
}
const bytes = await image.arrayBuffer();
const buffer = Buffer.from(bytes);
const path = join("/","public/uploads/blogs/cover", image.name);
await writeFile(path, buffer, (error) => {
return NextResponse.json({
success: false,
message: "File save unsuccessful. Please try again.",
});
});
const coverImagePath = join("/uploads/blogs/cover", image.name)
const blog = new Blog({
coverImagePath,
title,
content,
slug,
});
const savedBlog = await blog.save();
cache.del("blogs");
return NextResponse.json({
message: "Blog posted successfully",
success: true,
savedBlog,
});
} catch (error: any) {
return NextResponse.json({ error: error.message }, { status: 500 });
}
}
What I tried:
Using NextApiRequest/NextApiResponse: Says I cannot access .formData() as
Property 'formData' does not exist on type 'NextApiRequest'
.Using multer & store the image, but still I get the same error on console.