Model 就是負責處理資料與業務邏輯的地方,我們使用的資料庫為 MongoDB,並用 mongoose 套件來輔助設計,所以會依照 mongoose 的設計方法來實作 Model。
要使用資料庫就必須進行連線,這邊我們就建立一個名為 database
的資料夾,並包含了 database.ts
、 database.option.ts
以及 index.ts
這三個檔案,當前的資料夾結構:
├── src
| ├── index.ts
| ├── app.ts
| ├── app.routing.ts
| ├── + bases
| ├── + common/resonse
| ├── + exceptions
| ├── + main/api
| ├── + types
| ├── + environments
| ├── database //本篇新增
| | ├── index.ts //本篇新增
| | ├── database.ts //本篇新增
| | └── database.option.ts //本篇新增
| └── + validators
├── package.json
└── tsconfig.json
下方為 database.option.ts
的程式碼:
export const DATABASE_OPTIONS = {
useNewUrlParser: true,
useUnifiedTopology: true
};
下方為 database.ts
的程式碼:
import mongoose from 'mongoose';
import { DATABASE_OPTIONS } from './database.option';
export class Database {
public connect(): void {
mongoose.connect(
`mongodb+srv://${ process.env.DB_USER }:${ process.env.DB_PWD }@expressmvctodolist-clus.jjknt.gcp.mongodb.net/${ process.env.DB_NAME }?retryWrites=true&w=majority`,
DATABASE_OPTIONS
)
.then(() => console.log(`Database ${ process.env.DB_NAME } is connected.`))
.catch(err => console.error(err));
}
}
最後透過 index.ts
來統一管理:
export * from './database';
在 App
新增 launchDatabase()
並引入 Database
以及輸入下方程式碼:
import { Database } from './database';
public launchDatabase(): void {
const database = new Database();
database.connect();
}
並在 index.ts
中呼叫:
import { App } from './app';
import { DefaultException } from './exceptions/default.exception';
const bootstrap = () => {
const app = new App();
app.setException(DefaultException);
app.launchDatabase(); // 這裡
app.bootstrap();
};
bootstrap();
我們先建立一個名為 models
的資料夾,並包含 todo.model.ts
,還記得 mongoose 在設計 Model 時要先做什麼事嗎?沒錯,要建置 Schema,我們先在 todo.model.ts
裡設計 Schema:
import { model, Schema } from 'mongoose';
const TodoSchema = new Schema(
{
content: {
type: String,
required: true
},
completed: {
type: Boolean,
required: true
}
},
{
timestamps: true
}
);
timestamps
是啟用createdAt
與updatedAt
的配置
接著要建立 Model:
export const TodoModel = model('Todo', TodoSchema);
這樣就完成建立 Model 了,但在使用時無法知道有哪些欄位,因為 model()
預設是定義輸出 Document
型別,這時候我們就要寫 Interface 來處理這個問題,考量到還有共用欄位 createdAt
與 updatedAt
,所以在 types
資料夾下建立 model.type.ts
:
import { Document } from 'mongoose';
export interface CoreDocument extends Document {
createdAt: Date;
updatedAt: Date;
}
我們再回到 todo.model.ts
,在這裡建立 TodoModel
的 Interface,並繼承 CoreDocument
:
import { CoreDocument } from '../types/model.type';
export interface TodoDocument extends CoreDocument {
content: string;
completed: boolean;
};
最後就是指定 model()
定義的型別,整個 todo.model.ts
如下:
import { model, Schema } from 'mongoose';
import { CoreDocument } from '../types/model.type';
const TodoSchema = new Schema(
{
content: {
type: String,
required: true
},
completed: {
type: Boolean,
required: true
}
},
{
timestamps: true
}
);
export interface TodoDocument extends CoreDocument {
content: string;
completed: boolean;
};
export const TodoModel = model<TodoDocument>('Todo', TodoSchema);
當前資料夾結構如下:
├── src
| ├── index.ts
| ├── app.ts
| ├── app.routing.ts
| ├── + bases
| ├── + common/resonse
| ├── + exceptions
| ├── + main/api
| ├── models
| | └── todo.model.ts //本篇新增
| ├── types
| | ├── model.type.ts //本篇新增
| | └── response.type.ts
| ├── + environments
| ├── database //本篇新增
| | ├── index.ts //本篇新增
| | ├── database.ts //本篇新增
| | └── database.option.ts //本篇新增
| └── + validators
├── package.json
└── tsconfig.json
mongoose 本身就有提供 Model,我們只需要在上面擴展即可,十分方便。下一篇將會統合 MVC,製作一個簡單的 TodoList API Server,敬請期待!
不確定是不是版本造成的差異:在mongoose 7的時候,有一個 automatic type inferrence
。
能解決和上文中相同的問題:
這樣就完成建立 Model 了,但在使用時無法知道有哪些欄位
所以看起來其實不用建立TodoDocument就可以解決這個問題,於是也在想,額外建立TodoDocument會有其他的好處有助於維護程式碼嗎?
或是其實可以直接使用上述automatic type inferrence就可以了?
你好,在新版本的 mongoose 確實有自動型別推論的功能,若用起來沒問題可以直接使用,不過如果有問題,官方建議先回到本來的做法。
Mongoose:
If automatic type inference doesn't work for you, you can always fall back to document interface definitions.