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"
}
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
β 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;
π Understanding the User Model
1οΈβ£ Import Required Packages
import mongoose, { model, models, Schema } from "mongoose";
import bcrypt from "bcryptjs";
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
}
This is a TypeScript interface.
It defines the shape of user data.
Example user document
{
email: "user@email.com",
password: "hashedpassword"
}
_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}
);
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);
}
});
This is called a Mongoose middleware.
Before saving the user
β it checks if the password changed
β then hashes the password
Example
Plain password
mypassword123
Stored password
$2a$10$KJH8H2JSJSHHJSJHJ
This makes the application secure.
4οΈβ£ Create the Model
const User = models?.User || model<IUser>("User", userSchema);
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;
π 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"
}
π Video Controls
controls: { type: Boolean, default: true }
This controls the video player controls.
Example
true β play pause controls visible
false β controls hidden
π Video Transformation
transformation: {
height: { type: Number, default: VIDEO_DIMENSIONS.height },
width: { type: Number, default: VIDEO_DIMENSIONS.width },
quality: { type: Number, min: 1, max: 100 },
}
This object controls
- video width
- video height
- video quality
Example
transformation: {
width: 1080,
height: 1920,
quality: 80
}
π§© 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
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)