Open In App

How To Set Up Mongoose With Typescript In NextJS ?

Last Updated : 09 Jun, 2025
Comments
Improve
Suggest changes
Like Article
Like
Report

Combining Mongoose with TypeScript in a Next.js project is an intelligent approach to develop scalable, type-safe, and maintainable full-stack web applications. Mongoose offers a powerful ODM (Object Data Modeling) layer to interact with MongoDB, and TypeScript provides static typing to detect issues early and enhance developers productivity. The article will demonstrate the setup process for Mongoose with TypeScript in a Next.js project so your application is resilient, scalable, and maintainable.

Why Use Mongoose with TypeScript in Next.js?

  • Strong typing: TypeScript ensures your MongoDB schemas and queries are type-safe, reducing runtime errors.
  • Modern full-stack development: Next.js 13 offers server-side rendering and API routes that seamlessly integrate with Mongoose.
  • Scalable architecture: MongoDB’s flexible schema plus Mongoose’s schema enforcement fits well with scalable apps.
  • Improved developer experience: Autocompletion, better refactoring, and fewer bugs with TypeScript.

Step 1: Create a New Next.js App with TypeScript

Create your Next.js 13 app using the official CLI and navigate to the project folder:

npx create-next-app my-next-app
cd my-next-app

Step 2: Install the required dependencies

Install Mongoose, MongoDB native driver, TailwindCSS (for styling), and React Hook Form (for form handling):

npm install tailwindcss  react-hook-form mongoose mongodb

Step 3: Project Structure

Screenshot-2024-05-30-141047
Project Structure

Step 4: Configure Your MongoDB Connection

Add your MongoDB connection string securely in a .env.local file at the root of your project:

MONGOB_URI = mongodb+srv://yourMongodbnab:[email protected]/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: Uploading an Image to MongoDB using only NextJS 13.4.

JavaScript
// 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;
JavaScript
// 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;
JavaScript
// 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);
};
JavaScript
// 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;
JavaScript
//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;

Step 5: Run Your Next.js Development Server

Start your Next.js app locally:

npm run dev

Open your browser and visit:

http://localhost:3000

Output:

Screenshot-2024-05-21-142353
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

Configuring Mongoose with TypeScript within Next.js enables you to develop strong, type-safe, and scalable full-stack applications. With the configuration, you enjoy MongoDB's versatility, Mongoose's schema validation, and TypeScript's developer productivity. Using GridFS for file storage incorporates significant features to manage large files such as images or videos.


Next Article

Similar Reads