Mongoose 「蒙古斯」(/ˈmɒŋ.ɡuːs/)

We access MongoDB through Mongoose module. We create these ts file to access MongoDB. (Example: Next.js Project, npm install mongoose)

When you need to define an interface in TypeScript but don’t want it to affect the current scope, you can use declare interface. Such a declaration does not generate JavaScript code, but just lets TypeScript know that this type exists.

//interface.ts

export declare interface WeatherInterface {
    zip: string;
    weather: string;
    tempC: string;
    tempF: string;
    friends: string[];
}

The Mongoose is implment by JavaScript. When you declare the variable in the schema, the type is JavaScript Type. Because the TypeScipt is superset of JavaScript, so it can accept the JavaScript Type declear.

//schema.ts
import { Schema } from "mongoose";
import { WeatherInterface } from "./interface";

export const WeatherSchema = new Schema<WeatherInterface>({
    zip: {
        type: String, 
        required: true,
    },
    weather: {
        type: String,
        required: true,
    },
    tempC: {
        type: String,
        required: true,
    },
    tempF: {
        type: String,
        required: true,
    },
    friends: {
        type: [String],
        required: true,
    }
});;

This way of writing can avoid repeated definition of Model, If mongoose.models.Weather already exists, the existing Model will be used directly. If mongoose.models.Weather does not exist, use model() to create the Model and automatically add it to mongoose.models.

//model.ts
import mongoose, { model } from "mongoose";
import { WeatherInterface } from "./interface";
import { WeatherSchema } from "./schema";

export default mongoose.models.Weather ||
    model<WeatherInterface>("Weather", WeatherSchema);
//services.ts
import WeatherModel from  "./model";
import { WeatherInterface } from "./interface";

export async function storeDocument(doc: WeatherInterface): Promise<boolean> {
    try {
        await WeatherModel.create(doc);
    } catch (error) {
        return false;
    }
    return true;
}

export async function findByZip(paramZip: string): Promise<Array<WeatherInterface> | null> {
    try {
        return await WeatherModel.findOne({zip: paramZip});
    } catch (error) {
        console.log(error);
    }
    return [];
}

export async function updateByZip(paramZip: string, newData: WeatherInterface): Promise<boolean> {
    try {
        await WeatherModel.updateOne({zip: paramZip}, newData);
        return true;
    } catch (error) {
        console.log(error);
    }
    return false;
}

export async function deleteByZip(paramZip: string): Promise<boolean> {
    try {
        await WeatherModel.deleteOne({zip: paramZip});
        return true;
    } catch (error) {
        console.log(error);
    }
    return false;
}

Ues Mongoose to connect Database. (npm install mongodb-memory-server), Please note that when we make changes to the mongoose model, we store them directly in the database. It can be inferred that the relationship between the model and the database exists in the mongoose framework.

import mongoose from "mongoose";
import { MongoMemoryServer } from "mongodb-memory-server";
import { storeDocument } from "@/mongoose/weather/services";

let isConnected: boolean = false; // 用來追蹤是否已經有連線

async function dbConnect(): Promise<any | string> {
    // 如果已經連接過了,就直接返回已經建立的連線
    if (isConnected) {
        console.log("Using existing database connection");
        return mongoose.connection;
    }

    // 如果尚未建立連線,則創建新的連線
    try {
        const mongoServer = await MongoMemoryServer.create();
        const MONGO_URI = mongoServer.getUri();

        await mongoose.disconnect(); // 確保任何現有的連線已經斷開
        await mongoose.connect(MONGO_URI, {
            dbName: "Weather"
        });

        await storeDocument({
            zip: "96815",
            weather: "sunny",
            tempC: "25C",
            tempF: "70F",
            friends: ["96814", "96826"]
        });

        await storeDocument({
            zip: "96814",
            weather: "rainy",
            tempC: "20C",
            tempF: "68F",
            friends: ["96815", "96826"]
        });

        await storeDocument({
            zip: "96826",
            weather: "rainy",
            tempC: "30C",
            tempF: "86F",
            friends: ["96815", "96814"]
        });

        isConnected = true; // 設置連線成功的標誌
        console.log("New database connection established");

        return mongoose.connection;
    } catch (error) {
        console.error("Error connecting to database:", error);
        throw error;
    }
}

export default dbConnect;