DEV Community

Cover image for πŸ“¦ Designing Models in Next.js using Mongoose
Himanay Khajuria
Himanay Khajuria

Posted on

πŸ“¦ Designing Models in Next.js using Mongoose

When we build a full stack Next.js application, we usually store data in a database.

But before saving data we must define how the data should look.

This structure is called a Model.

In this blog we will understand:

βœ” What a model is

βœ” Why models are important

βœ” How to create models in Next.js using Mongoose

βœ” Example models for User and Video


🧠 What is a Model

A model defines the structure of data stored in the database.

Think of it like a blueprint of data.

For example if we store users in a database we may want data like:

{
  email: "user@email.com",
  password: "123456"
}
Enter fullscreen mode Exit fullscreen mode

Instead of storing random data we define rules like:

  • email must be required
  • password must be required
  • email must be unique

This is what a model does.


βš™οΈ Installing bcrypt for Password Security

When storing passwords we should never store plain text passwords.

We use bcrypt to convert the password into a hashed password.

Install the packages

npm i bcryptjs
npm i --save-dev @types/bcryptjs
Enter fullscreen mode Exit fullscreen mode

βœ” bcryptjs helps hash passwords

βœ” @types/bcryptjs provides TypeScript support


πŸ‘€ User Model in Next.js

Let us look at a simple User model.

import mongoose, { model, models, Schema } from "mongoose";
import bcrypt from "bcryptjs";

export interface IUser {
    email: string;
    password: string;
    _id?: mongoose.Types.ObjectId;
    createdAt?: Date;
    updatedAt?: Date
}

const userSchema = new Schema<IUser>(
    {
        email: {type: String, required: true, unique: true},
        password: {type: String, required: true},
    },
    {timestamps: true}
);

userSchema.pre("save", async function () {
    if (this.isModified("password")) {
        this.password = await bcrypt.hash(this.password, 10);
    }
});

const User = models?.User || model<IUser>("User", userSchema);

export default User;
Enter fullscreen mode Exit fullscreen mode

πŸ” Understanding the User Model

1️⃣ Import Required Packages

import mongoose, { model, models, Schema } from "mongoose";
import bcrypt from "bcryptjs";
Enter fullscreen mode Exit fullscreen mode

Here we import

  • mongoose to create the schema
  • bcrypt to hash the password

2️⃣ Define User Interface

export interface IUser {
    email: string;
    password: string;
    _id?: mongoose.Types.ObjectId;
    createdAt?: Date;
    updatedAt?: Date
}
Enter fullscreen mode Exit fullscreen mode

This is a TypeScript interface.

It defines the shape of user data.

Example user document

{
 email: "user@email.com",
 password: "hashedpassword"
}
Enter fullscreen mode Exit fullscreen mode

_id, createdAt, updatedAt are optional.


3️⃣ Create User Schema

const userSchema = new Schema<IUser>(
{
 email: {type: String, required: true, unique: true},
 password: {type: String, required: true},
},
{timestamps: true}
);
Enter fullscreen mode Exit fullscreen mode

This schema defines rules for the database.

Rules here

  • email

    • must be a string
    • must be unique
    • required field
  • password

    • must be a string
    • required field

timestamps: true automatically adds

  • createdAt
  • updatedAt

πŸ” Hash Password Before Saving

userSchema.pre("save", async function () {
 if (this.isModified("password")) {
   this.password = await bcrypt.hash(this.password, 10);
 }
});
Enter fullscreen mode Exit fullscreen mode

This is called a Mongoose middleware.

Before saving the user

βœ” it checks if the password changed

βœ” then hashes the password

Example

Plain password

mypassword123
Enter fullscreen mode Exit fullscreen mode

Stored password

$2a$10$KJH8H2JSJSHHJSJHJ
Enter fullscreen mode Exit fullscreen mode

This makes the application secure.


4️⃣ Create the Model

const User = models?.User || model<IUser>("User", userSchema);
Enter fullscreen mode Exit fullscreen mode

This line prevents model duplication in Next.js development.

Next.js reloads many times so we check

βœ” if model already exists

βœ” otherwise create a new one


🎬 Video Model Example

Now let us see another model for storing videos.

import mongoose, { model, models, Schema } from "mongoose";

export const VIDEO_DIMENSIONS = {
    width: 1080,
    height: 1920,
} as const

export interface IVideo {
  _id?: mongoose.Types.ObjectId;
  createdAt?: Date;
  updatedAt?: Date
  title: string;
  description: string;
  videoUrl: string;
  thumbnailUrl: string;
  controls?: boolean;
  transformation?: {
    height: number;
    width: number;
    quality?: number;
  };
}

const videoSchema = new Schema<IVideo>(
{
 title: { type: String, required: true },
 description: { type: String, required: true },
 videoUrl: { type: String, required: true },
 thumbnailUrl: { type: String, required: true },
 controls: { type: Boolean, default: true },
 transformation: {
   height: { type: Number, default: VIDEO_DIMENSIONS.height },
   width: { type: Number, default: VIDEO_DIMENSIONS.width },
   quality: { type: Number, min: 1, max: 100 },
 },
},
{ timestamps: true },
);

const Video = models?.Video || model<IVideo>("Video", videoSchema);

export default Video;
Enter fullscreen mode Exit fullscreen mode

πŸ” Understanding the Video Model

πŸ“Ή Video Information

The video model stores data like

  • title
  • description
  • video URL
  • thumbnail URL

Example document

{
 title: "Nextjs Tutorial",
 description: "Learn Nextjs basics",
 videoUrl: "https://example.com/video.mp4",
 thumbnailUrl: "https://example.com/thumb.jpg"
}
Enter fullscreen mode Exit fullscreen mode

πŸŽ› Video Controls

controls: { type: Boolean, default: true }
Enter fullscreen mode Exit fullscreen mode

This controls the video player controls.

Example

true β†’ play pause controls visible
false β†’ controls hidden
Enter fullscreen mode Exit fullscreen mode

πŸ“ Video Transformation

transformation: {
 height: { type: Number, default: VIDEO_DIMENSIONS.height },
 width: { type: Number, default: VIDEO_DIMENSIONS.width },
 quality: { type: Number, min: 1, max: 100 },
}
Enter fullscreen mode Exit fullscreen mode

This object controls

  • video width
  • video height
  • video quality

Example

transformation: {
 width: 1080,
 height: 1920,
 quality: 80
}
Enter fullscreen mode Exit fullscreen mode

🧩 Why Models are Important

Models help us

βœ” keep data structured

βœ” validate data before saving

βœ” prevent invalid data

βœ” make database queries easier

Without models a database can become messy and difficult to manage.


πŸ“ Typical Folder Structure

Many Next.js projects keep models like this

/models
   User.ts
   Video.ts
/lib
   db.ts
Enter fullscreen mode Exit fullscreen mode

This keeps the project clean and organized.


πŸ“š Summary

In this blog we learned

βœ” what a model is

βœ” how to design models using Mongoose

βœ” how to create a User model

βœ” how to secure passwords using bcrypt

βœ” how to create a Video model

Designing good models is an important step when building full stack applications with Next.js and MongoDB.


Happy Coding πŸ‘©πŸ»β€πŸ’»
Keep building and keep learning.....

Top comments (0)