Skip to content

Commit 6569e17

Browse files
committed
Implement Database models
1 parent 246dc7f commit 6569e17

File tree

6 files changed

+385
-3
lines changed

6 files changed

+385
-3
lines changed

database/booking.model.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { Schema, model, models, Document } from "mongoose";
2+
import Event from "./event.model";
3+
4+
// Interface for the Booking document
5+
export interface IBooking extends Document {
6+
eventId: Schema.Types.ObjectId;
7+
email: string;
8+
createdAt: Date;
9+
updatedAt: Date;
10+
}
11+
12+
const bookingSchema = new Schema<IBooking>(
13+
{
14+
eventId: {
15+
type: Schema.Types.ObjectId,
16+
ref: "Event",
17+
required: [true, "Event ID is required"],
18+
},
19+
email: {
20+
type: String,
21+
required: [true, "Email is required"],
22+
match: [/.+\@.+\..+/, "Please enter a valid email address"],
23+
},
24+
},
25+
{
26+
timestamps: true, // Enable automatic createdAt and updatedAt fields
27+
}
28+
);
29+
30+
// Pre-save hook to verify that the referenced eventId exists
31+
bookingSchema.pre<IBooking>("save", async function (next) {
32+
if (this.isNew) {
33+
const eventExists = await Event.findById(this.eventId);
34+
if (!eventExists) {
35+
throw new Error("Event not found");
36+
}
37+
}
38+
next();
39+
});
40+
41+
// Add an index on eventId for faster queries
42+
bookingSchema.index({ eventId: 1 });
43+
44+
const Booking = models.Booking || model<IBooking>("Booking", bookingSchema);
45+
46+
export default Booking;

database/event.model.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { Schema, model, models, Document } from "mongoose";
2+
import slugify from "slugify";
3+
4+
// Interface for the Event document
5+
export interface IEvent extends Document {
6+
title: string;
7+
slug: string;
8+
description: string;
9+
overview: string;
10+
image: string;
11+
venue: string;
12+
location: string;
13+
date: string;
14+
time: string;
15+
mode: "online" | "offline" | "hybrid";
16+
audience: string;
17+
agenda: string[];
18+
organizer: string;
19+
tags: string[];
20+
createdAt: Date;
21+
updatedAt: Date;
22+
}
23+
24+
const eventSchema = new Schema<IEvent>(
25+
{
26+
title: { type: String, required: [true, "Title is required"] },
27+
slug: { type: String, unique: true },
28+
description: { type: String, required: [true, "Description is required"] },
29+
overview: { type: String, required: [true, "Overview is required"] },
30+
image: { type: String, required: [true, "Image is required"] },
31+
venue: { type: String, required: [true, "Venue is required"] },
32+
location: { type: String, required: [true, "Location is required"] },
33+
date: { type: String, required: [true, "Date is required"] },
34+
time: { type: String, required: [true, "Time is required"] },
35+
mode: {
36+
type: String,
37+
enum: ["online", "offline", "hybrid"],
38+
required: [true, "Mode is required"],
39+
},
40+
audience: { type: String, required: [true, "Audience is required"] },
41+
agenda: { type: [String], required: [true, "Agenda is required"] },
42+
organizer: { type: String, required: [true, "Organizer is required"] },
43+
tags: { type: [String], required: [true, "Tags are required"] },
44+
},
45+
{
46+
timestamps: true, // Enable automatic createdAt and updatedAt fields
47+
}
48+
);
49+
50+
// Pre-save hook for slug generation and data normalization
51+
eventSchema.pre<IEvent>("save", function (next) {
52+
// Generate a URL-friendly slug from the title if it has been modified
53+
if (this.isModified("title")) {
54+
this.slug = slugify(this.title, { lower: true, strict: true });
55+
}
56+
57+
// Validate and normalize the date to ISO format
58+
if (this.date) {
59+
const isoDate = new Date(this.date).toISOString();
60+
this.date = isoDate;
61+
}
62+
63+
next();
64+
});
65+
66+
// Add a unique index to the slug field for faster queries
67+
eventSchema.index({ slug: 1 });
68+
69+
const Event = models.Event || model<IEvent>("Event", eventSchema);
70+
71+
export default Event;

database/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import Event from "./event.model";
2+
import Booking from "./booking.model";
3+
4+
export { Event, Booking };

lib/mongodb.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import mongoose, { Mongoose } from "mongoose";
2+
3+
const MONGODB_URI = process.env.MONGODB_URI;
4+
5+
if (!MONGODB_URI) {
6+
throw new Error(
7+
"Please define the MONGODB_URI environment variable inside .env.local"
8+
);
9+
}
10+
11+
/**
12+
* Global is used here to maintain a cached connection across hot reloads
13+
* in development. This prevents connections from growing exponentially
14+
* during API Route usage.
15+
*/
16+
interface MongooseCache {
17+
conn: Mongoose | null;
18+
promise: Promise<Mongoose> | null;
19+
}
20+
21+
// Extend NodeJS.Global to include mongoose property
22+
declare global {
23+
// eslint-disable-next-line no-var
24+
var mongoose: MongooseCache | undefined;
25+
}
26+
27+
let cached: MongooseCache = global.mongoose ?? { conn: null, promise: null };
28+
29+
if (!cached) {
30+
cached = (global as typeof globalThis).mongoose = {
31+
conn: null,
32+
promise: null,
33+
};
34+
}
35+
36+
async function connectDB() {
37+
// If a connection is already cached, use it
38+
if (cached.conn) {
39+
return cached.conn;
40+
}
41+
42+
// If a connection promise doesn't exist, create one
43+
if (!cached.promise) {
44+
const opts = {
45+
bufferCommands: false,
46+
};
47+
48+
cached.promise = mongoose.connect(MONGODB_URI!, opts);
49+
}
50+
// Wait for the connection promise to resolve and cache the connection
51+
cached.conn = await cached.promise;
52+
return cached.conn;
53+
}
54+
55+
export default connectDB;

0 commit comments

Comments
 (0)