How To Set Up Mongoose With Typescript In NextJS ?
Integrating Mongoose with TypeScript in a Next.js application allows you to harness the power of MongoDB while maintaining strong typing and modern JavaScript features. This guide will walk you through the process of setting up Mongoose with TypeScript in a Next.js project, ensuring your application is robust, scalable, and easy to maintain.
Prerequisites:
Steps to Create a NextJS App and Installing Module
Step 1: Initialize Next app and move to the project directory.
npx create-next-app my-next-app
cd my-next-app
Step 2: Install the required dependencies:
npm install tailwindcss react-hook-form mongoose mongodb
Project Structure

Step 3: Setting Up MongoDB Connection inside .env file add:
MONGOB_URI = mongodb+srv://yourMongodbnab:yourpassword@cluster0.ldnz1.mongodb.net/DatabaseName
The Updated dependencies of your package.json file will look like this:
"dependencies": {
"mongoose": "^8.4.0",
"next": "13.4",
"react": "^18",
"react-dom": "^18"
}
Example: Beow is an example of uploading an Image to MongoDB using only NextJS 13.4.
// utils/connectToDb.ts
import mongoose, { Connection } from "mongoose";
import { GridFSBucket } from "mongodb";
let client: Connection | null = null;
let bucket: GridFSBucket | null = null;
const MONGODB_URI = process.env.MONGODB_URI as string;
interface DbConnection {
client: Connection;
bucket: GridFSBucket;
}
async function connectToDb(): Promise<DbConnection> {
if (client) {
return { client, bucket: bucket as GridFSBucket };
}
await mongoose.connect(MONGODB_URI);
client = mongoose.connection;
bucket = new mongoose.mongo.GridFSBucket(client.db, {
bucketName: "images",
});
console.log("Connected to the Database");
return { client, bucket };
}
export default connectToDb;
// utils/posts.ts
import mongoose, { Schema, Document, Model } from "mongoose";
interface IPost extends Document {
name: string;
imageUrl: string;
}
const postsSchema: Schema<IPost> = new Schema({
name: { type: String, required: true },
imageUrl: { type: String, required: true },
});
const PostModel: Model<IPost> = mongoose.models.Posts || mongoose.model<IPost>("Posts", postsSchema);
export default PostModel;
// api/route.tsx
import { NextResponse } from "next/server";
import { Readable } from "stream";
import Posts from "../../utils/posts";
import connectToDb from "../../utils/connectToDb";
export const revalidate = 0;
export const POST = async (req: Request) => {
const { client, bucket } = await connectToDb();
let name: string | undefined;
let image: string | undefined;
const formData = await req.formData();
console.log(formData);
for (const entries of Array.from(formData.entries())) {
const [key, value] = entries;
if (key === "name") {
name = value as string;
}
if (typeof value === "object" && value instanceof File) {
image = Date.now() + value.name;
console.log("done");
const buffer = Buffer.from(await value.arrayBuffer());
const stream = Readable.from(buffer);
const uploadStream = bucket.openUploadStream(image, {});
stream.pipe(uploadStream);
await new Promise((resolve, reject) => {
uploadStream.on('finish', resolve);
uploadStream.on('error', reject);
});
}
}
if (name && image) {
const newItem = new Posts({
name,
imageUrl: image,
});
await newItem.save();
} else {
return NextResponse.json(
{ msg: "Invalid form data" },
{ status: 400 }
);
}
return NextResponse.json({ msg: "ok" });
};
export const GET = async () => {
await connectToDb();
const posts = await Posts.find({});
return NextResponse.json(posts);
};
// components/newPost.tsx
"use client";
import Image from "next/image";
import { useRouter } from "next/navigation";
import { useState, ChangeEvent } from "react";
import {
useForm,
SubmitHandler,
} from "react-hook-form";
interface FormData {
name: string;
imageUrl: FileList;
}
const NewPost = () => {
const { register, handleSubmit, reset } =
useForm<FormData>();
const [previewImage, setPreviewImage] =
useState<string | null>(null);
const router = useRouter();
const handleFileChange = (
e: ChangeEvent<HTMLInputElement>
) => {
const file = e.target.files
? e.target.files[0]
: null;
if (file) {
const reader = new FileReader();
reader.onloadend = () => {
setPreviewImage(
reader.result as string
);
};
reader.readAsDataURL(file);
} else {
setPreviewImage(null);
}
};
const onSubmit: SubmitHandler<
FormData
> = async (data) => {
const formData = new FormData();
formData.append("name", data.name);
if (data.imageUrl) {
for (
let i = 0;
i < data.imageUrl.length;
i++
) {
formData.append(
"imageUrl",
data.imageUrl[i]
);
}
}
await fetch("/api", {
method: "POST",
body: formData,
});
// Clear form data and reset input fields
setPreviewImage(null);
reset();
router.refresh();
};
return (
<main className="flex flex-col items-center
justify-between ">
<div className="max-w-md mx-auto">
<form
className="bg-white shadow-md
rounded px-8 pt-6 pb-8 mb-4"
onSubmit={handleSubmit(
onSubmit
)}
>
<div className="mb-4">
<label
className="block text-gray-700
text-sm font-bold mb-2"
htmlFor="input1"
>
Text Input 1
</label>
<input
className="shadow appearance-none
border rounded w-full py-2 px-3
text-gray-700 leading-tight
focus:outline-none focus:shadow-outline"
id="input1"
type="text"
placeholder="Enter text input 1"
{...register("name")}
/>
</div>
<div className="mb-4">
<label
className="block text-gray-700
text-sm font-bold mb-2"
htmlFor="fileInput"
>
File Input
</label>
<input
type="file"
accept="image/*"
className="file-input
file-input-bordered w-full max-w-xs"
id="fileInput"
{...register(
"imageUrl"
)}
onChange={
handleFileChange
}
/>
{previewImage && (
<Image
width={200}
height={200}
src={previewImage}
alt="Preview"
className="mt-2 w-full h-32 object-cover"
/>
)}
</div>
<div className="flex items-center justify-between">
<button
className="bg-blue-500 hover:bg-blue-700
text-white font-bold py-2 px-4
rounded focus:outline-none focus:shadow-outline"
type="submit"
>
Submit
</button>
</div>
</form>
</div>
</main>
);
};
export default NewPost;
//page.tsx
import NewPost from "./components/newPost";
import React from "react";
const Home: React.FC = () => {
return (
<div className="">
<div className="my-20">
<NewPost />
</div>
</div>
);
};
export default Home;
Start your Next.js development server:
npm run dev
Visit http://localhost:3000 in your browser to see the application in action.
Output:

When you enter the file name, upload a file, and then click on the submit button, your file is successfully uploaded to MongoDB.
Conclusion
Overall, using Mongoose in Next.js empowers developers to build feature-rich, data-driven web applications with a modern frontend and a robust backend database, enabling seamless integration, efficient data management, and scalable architecture.