TypeScript is an object-oriented programming language, and it is a superset of JavaScript and contains all of its elements. By using TSC (TypeScript Compiler), we can convert Typescript code (.ts file) to JavaScript (.js file). It is open-source and its code is easier to read and understand.
MongoDB is a versatile widely used NoSQL documentation based database. A collection can hold a bunch of documents. We can assume a collection as a table in RDBMS technology, but no strong entity relationships are required in the collection. A mongoose schema is equivalent to a collection. We can keep different data inside a Mongoose schema to create the collection in MongoDB. In this article, let us see how to make Mongo schema from pure Typescript class. The extension of the Typescript file is .ts and usually, from Angular framework projects, we are using Typescript classes, and let us see in detail. In order to interact with MongoDB, we require Mongoose.
Project Setup and Module Installation:
Step 1: We are going to use the Typescript class, let us create by initializing a project using the following command.
yarn init (or npm init )
When prompted, set the entry point to the following file.
src/server.ts .
Step 2: Install the required module using the following command.
# Dependency to connect to create MongoDB # Schema and connect to mongodb yarn add express mongoose # Dependency to create TypeScript files, # Dependency to execute those files in Node # Dependency to speed up the development yarn add -D nodemon typescript ts-node @types/express @types/mongoose @types/node # GlobalDependencies npm i -g typescript ts-node
package.json: Our package.json file will look like this.
{ "name": "mongo-typescript", "version": "1.0.0", "main": "src/server.ts", "license": "MIT", "scripts": { "start": "node --inspect=5858 -r ts-node/register ./src/server.ts", "dev": "nodemon", "build": "tsc", "script": "cd src/scripts && ts-node" }, "dependencies": { "express": "^4.17.1", "mongoose": "^5.9.7" }, "devDependencies": { "@types/express": "^4.17.6", "@types/mongoose": "^5.7.10", "@types/node": "^13.11.1", "nodemon": "^2.0.3", "ts-node": "^8.8.2", "typescript": "^3.8.3" } }
tsconfig.json: This file has to be created in the root directory, and it will tell the compiler about the location for TypeScript files, and it will allow us to use ES6 import/export syntax.
{ "compilerOptions": { "target": "es6", "module": "commonjs", "outDir": "dist", "sourceMap": true }, "include": ["src/**/*.ts"], "exclude": ["node_modules", ".vscode"] }
Connecting to the Database: For database connectivity, Create a separate folder using the following commands.
mkdir src cd src mkdir database touch database.ts
Project Structure: It will look like this.
Example:
database.ts
import * as Mongoose from 'mongoose' ; import { EmployeeModel } from './employees/employees.model' ; let database: Mongoose.Connection; export const connect = () => { // Add your own uri below, here my dbname is UserDB // and we are using the local mongodb const uri = if (database) { return ; } // In order to fix all the deprecation warnings, // below are needed while connecting Mongoose.connect(uri, { useNewUrlParser: true , useFindAndModify: true , useUnifiedTopology: true , useCreateIndex: true , }); database = Mongoose.connection; // When mentioned database is available and successfully connects database.once( 'open' , async () => { console.log( 'Connected to database successfully' ); }); // In case of any error database.on( 'error' , () => { console.log(`Error connecting to database. Check Whether mongoDB installed or you can try to give opensource Mongo Atlas database`); }); return { EmployeeModel }; }; // Safer way to get disconnected export const disconnect = () => { if (!database) { return ; } Mongoose.disconnect(); }; |
server.ts
import * as express from "express" ; import { connect } from "./database/database" ; const app = express(); // Available port number and it should not // be used by other services const port = 5002; connect(); // To indicate the project initiated, // let us add console message app.listen(port, () => { console.log(`Server started on http: //localhost:${port}`); }); |
Create models
Typescript files are helpful to create Mongo Schema. It is mentioned as Three parts to a Mongoose model (schema, static methods, and instance methods), Fourth one is to hold our TypeScript interfaces, and the fifth to bring everything together.
Inside the src/database/<collectionname> folder, let us create the models. Here <<collectionname> represents the name of MongoDB collection. Let us have it as employees.
- <collectionname>.schema.ts: Define the Mongoose Schema, which helps for the determination of the shape of our MongoDB documents.
- <collectionname>.statics.ts: Required static methods are added and called on the model itself.
- <collectionname>.methods.ts: Instance methods of our model, functions which can be called on individual model instances.
- <collectionname>.types.ts: Store the types we’ll use in the other files.
- <collectionname>.model.ts: It is used to combine everything together.
We can use the following command to create a directory and files.
# Move to scr folder and create database folder cd src mkdir database # Move to database folder and create employees folder cd database mkdir employees # Move to employees folder and create files cd employees touch employees.schema.ts employees.statics.ts touch employees.methods.ts employees.types.ts employees.model.ts
employees.schema.ts
// Main schema file where we can define the // required attributes import * as Mongoose from "mongoose" ; const EmployeeSchema = new Mongoose.Schema({ firstName: String, lastName: String, age: Number, dateOfJoining: { type: Date, default : new Date(), }, lastUpdated: { type: Date, default : new Date(), }, gender: String, department: String, // All other required attributes // should be given here }); export default EmployeeSchema; |
- IEmployeeDocument: Inclusion of our fields and other elements of a standard Mongoose Document.
- IEmployeeModel: Representation of a standard Mongoose Model, containing documents of our IEmployeeDocument type.
employees.types.ts
import { Document, Model } from "mongoose" ; // These fields represent fields of collection // and name of the collection is Employee export interface IEmployee { firstName: string; lastName: string; age: number; dateOfEntry?: Date; lastUpdated?: Date; gender: String; department: String; } export interface IEmployeeDocument extends IEmployee, Document { } export interface IEmployeeModel extends Model<IEmployeeDocument> { } |
employees.model.ts
import { model } from "mongoose" ; import { IEmployeeDocument } from "./employees.types" ; import EmployeeSchema from "./employees.schema" ; export const EmployeeModel = model<IEmployeeDocument>( "employee" , EmployeeSchema ) |
Create two static methods as shown below:
- findOneOrCreate: Checks for an entry for its existence and if not, creates a new entry.
- findByAge: Returns an array of employees, based on a provided age range.
Similarly, we can define methods depends upon the requirement like findByGender, findByDepartment, etc., which ultimately matches our requirements
employees.statics.ts
import { IEmployeeDocument, IEmployeeModel } from "./employees.types" ; // Check for the existence of an entry // and if it is not available, create one export async function findOneOrCreate( this : IEmployeeModel, { firstName, lastName, age, gender, department, }: { firstName: string; lastName: string; age: number; gender: string; department: string } ): Promise<IEmployeeDocument> { const employeeRecord = await this .findOne({ firstName, lastName, age, gender, department }); if (employeeRecord) { return employeeRecord; } else { return this .create({ firstName, lastName, age, gender, department }); } } export async function findByAge( this : IEmployeeModel, min?: number, max?: number ): Promise<IEmployeeDocument[]> { return this .find({ age: { $gte: min || 0, $lte: max || Infinity } }); } // Similarly add the rest of the methods according to the requirement |
employees.methods.ts
import { Document } from "mongoose" ; import { IEmployeeDocument } from "./employees.types" ; export async function setLastUpdated( this : IEmployeeDocument): Promise<void> { const now = new Date(); if (! this .lastUpdated || this .lastUpdated < now) { this .lastUpdated = now; await this .save(); } } export async function sameLastName( this : IEmployeeDocument): Promise<Document[]> { return this .model( "employee" ) .find({ lastName: this .lastName }); } |
employees.schema.ts
import * as Mongoose from "mongoose" ; import { findOneOrCreate, findByAge } from "./employees.statics" ; import { setLastUpdated, sameLastName } from "./employees.methods" ; const EmployeeSchema = new Mongoose.Schema({ firstName: String, lastName: String, age: Number, dateOfJoining: { type: Date, default : new Date(), }, lastUpdated: { type: Date, default : new Date(), }, gender: String, department: String, // All other required attributes should be given here }); EmployeeSchema.statics.findOneOrCreate = findOneOrCreate; EmployeeSchema.statics.findByAge = findByAge; EmployeeSchema.methods.setLastUpdated = setLastUpdated; EmployeeSchema.methods.sameLastName = sameLastName; export default EmployeeSchema; |
employees.types.ts
import { Document, Model } from "mongoose" ; export interface IEmployee { firstName: string; lastName: string; age: number; dateOfJoining?: Date; lastUpdated?: Date; gender: String; department: String; } export interface IEmployeeDocument extends IEmployee, Document { setLastUpdated: ( this : IEmployeeDocument) => Promise<void>; sameLastName: ( this : IEmployeeDocument) => Promise<Document[]>; } export interface IEmployeeModel extends Model<IEmployeeDocument> { findOneOrCreate: ( this : IEmployeeModel, { firstName, lastName, age, gender, department, }: { firstName: string; lastName: string; age: number; gender: string; department: string; } ) => Promise<IEmployeeDocument>; findByAge: ( this : IEmployeeModel, min?: number, max?: number ) => Promise<IEmployeeDocument[]>; } |
sampleEmployeeData.ts
import { EmployeeModel } from "../database/employees/employees.model" ; import { connect, disconnect } from "../database/database" ; (async () => { connect(); // Via "sampleEmployeeData.ts" we can add data to Mongoose schema // Our schema name is employees const employees = [ { firstName: "Rachel" , lastName: "Green" , age: 25, gender: "Female" , department: "Design" }, { firstName: "Monica" , lastName: "Geller" , age: 25, gender: "Female" , department: "Catering" }, { firstName: "Phebe" , lastName: "Phebe" , age: 25, gender: "Female" , department: "Masus" }, { firstName: "Ross" , lastName: "Geller" , age: 30, gender: "Male" , department: "Paleontology" }, { firstName: "Chandler" , lastName: "Bing" , age: 30, gender: "Male" , department: "IT" }, { firstName: "Joey" , lastName: "Joey" , age: 30, gender: "Male" , department: "Dramatist" }, ]; try { for (const employee of employees) { await EmployeeModel.create(employee); console.log(`Created employee ${employee.firstName} ${employee.lastName}`); } disconnect(); } catch (e) { console.log(e); } })(); |
Steps to run the application: Start the server using the following command.
yarn start
The next step is we need to create the collections using the Mongoose schema. Use the following command to fill in the data:
yarn script sampleEmployeeData.ts
Output:
In the Mongo shell, we can verify the same. In UserDB database, on querying db.employees.find().pretty(), we can see the output:
Conclusion: Typescript files are so cool which has advanced features of JavaScript and by using mongoose, Express we can easily create MongoDB schemas by using Typescript files