31-Nodejs Course 2023: Database Models: Create Base Model
Tags: node, typescript, mongodb, fastify
Published: 2022-11-03 10:59:44

---
title: 31-Nodejs Course 2023: Database Models: Create Base Model
published: true
description: 
series: Nodejs Course 2023
tags: nodejs, typescript, mongodb, fastify
#cover_image: 
# Use a ratio of 100:42 for best results.
---

So we got to know about database models in the previous article, now let's create a base model that we'll use to create other models.

## Model Structure

Before we getting started, let's illustrate how the model structure works, we have 4 main parts:

- Static Variables: These variables will be access directly without creating an instance of the model, for example, the collection name.
- Static Methods: These methods will be access directly without creating an instance of the model, for example, the `find` method.
- Instance Variables: These variables will be access after creating an instance of the model, for example, the `name` variable.
- Instance Methods: These methods will be access after creating an instance of the model, for example, the `save` method.

Static variables will be mainly for the meta data of the model which will be used to collect information about the model like the collection name, the initial id value and so on.

The static method will be used for quick access and operations that are not related to a specific instance of the model, for example, the `find` method.

## Base Model

Go to `src/core/database` folder and create `model` folder inside it then create `model.ts` file.

```ts
// src/core/database/model/model.ts
export default abstract class Model {
  /**
   * Collection Name
   */
  public static collectionName = "";
}
```

Nothing fancy here, just a base model class that we'll extend in other models.

We're making it abstract because we don't want to use it directly, we'll use it as a base class for other models to inherit from.

The collection name will be a static property that we'll override in the child classes so we can access the collection name from the model class directly without creating an instance of it.

You might wonder that we did a file called `model.ts` inside `model` directory not `index.ts` that's because we're going to create more files inside the `model` directory so we'll make the `index.ts` file for the exports only.

Now let's create the index.file and export the base model from it.

```ts
// src/core/database/model/index.ts
export { default as Model } from './model';
```

Then go to `database/index.ts` file and export all from the `model/index.ts` fike.

```ts
// src/core/database/index.ts
export * from './model';
```

This will allow us to access the `Model` class by importing it like this:

```ts
import { Model } from 'core/database';
```

## Users Model

Now let's create a new file called `user.ts` in `src/app/users/models` directory.

```ts
// src/app/users/models/user.ts
import { Model } from 'core/database';

export default class User extends Model {
  /**
   * Collection Name
   */
  public static collectionName = "users";
}
```

Now we have a base model and a user model that extends it then we added the collection name to the user model.

Pretty easy, nothing fancy here but still these are just an empty files without soul (not database yet).

We can now get the collection name directly like this:

```ts
import User from 'app/users/models/user';

console.log(User.collectionName); // users
```

Let's create its soul.

## Database Connection

Heading back to our base model, let's define a database connection property.

```ts
// src/core/database/model/model.ts
import connection, { Connection } from './../connection';

export default abstract class Model {
  /**
   * Collection Name
   */
  public static collectionName = "";

  /**
   * Database Connection
   */
  public static connection: Connection = connection;
}
```

We'll use our own connection so we can use it to access the connection (If needed) and we can of course access the database handler directly from it.

## Collection Query

Now let's define a collection method to return the collection query handler.

```ts
// src/core/database/model/model.ts
import { Collection } from "mongodb";
import connection, { Connection } from './../connection';

export default abstract class Model {
  /**
   * Collection Name
   */
  public static collectionName = "";

  /**
   * Database Connection
   */
  public static connection: Connection = connection;

  /**
   * Get Collection query
   */
  public static query(): Collection {
    return this.connection.database.collection(this.collectionName);
  }
}
```

In javascript, accessing static properties can be used with `this` keyword.

We need to make sure that the connection's database property is always exists as it may throw an error as it is possibly undefined.

```ts
// src/core/database/connection.ts
  /**
   * Database instance
   */
  // 👇🏻Replace the ?: with !:
  public database!: Database;
```

So far so good, now we're don with static methods, let's implement the non static ones that we need.

At this point, we can now access the users collection like this:

```ts
import User from 'app/users/models/user';

// get the collection query
const usersCollection = User.query();

await usersCollection.find().toArray();
```

## Accessing Child Static Property From Parent Class

In the model it self when we create a new instance (object) of it, we need to access the collection of the model to make internal operations, like saving the user's data to the database, so we need to access the `query` method from the child class.

We can't access the static property of `a child class from the parent class` if we created an instance of that child, so we need to do a trick to access it.

To access the child class static members, we'll use `this.constructor` feature.

```ts
// src/core/database/model/model.ts
import connection, { Connection } from './../connection';

export default abstract class Model {
  /**
   * Collection Name
   */
  public static collectionName = "";

  /**
   * Database Connection
   */
  public static connection: Connection = connection;

  /**
   * Get Collection Name
   */
  public getCollectionName(): string {
    return this.constructor.collectionName;
  }
}
```

But Typescript will start complaining, because it doesn't know that the `constructor` property is a class.

To fix this, we'll use the `typeof` keyword to tell Typescript that the `constructor` property is a class.

```ts
// src/core/database/model/model.ts

export default abstract class Model {
  // ...

  /**
   * Get Collection Name
   */
  public getCollectionName(): string {
    return (this.constructor as typeof Model).collectionName;
  }
}
```

We can wrap it in a private method so we can use it in other methods by passing only the static property that we need to get.

```ts
// src/core/database/model/model.ts

export default abstract class Model {
  // ...

  /**
   * Get Static Property
   */
  protected getStaticProperty<T>(property: keyof typeof Model): T {
    return (this.constructor as any)[property];
  }

  /**
   * Get Collection Name
   */
  public getCollectionName(): string {
    return this.getStaticProperty('collectionName');
  }
}
```

The `keyof typeof Model` will tell Typescript that the `property` parameter is a key of the `Model` class so we can use the auto complete feature to get the static property that we need to get.

## Getting Connection, Database and Collection Query

Now we can use the `getStaticProperty` method to get the connection, database and collection query.

```ts
// src/core/database/model/model.ts

export default abstract class Model {
  // ...
  /**
   * Get Collection Name
   */
  public getCollectionName(): string {
    return this.getStaticProperty("collectionName");
  }

  /**
   * Get database connection
   */
  public getConnection(): Connection {
    return this.getStaticProperty("connection");
  }

  /**
   * Get database instance
   */
  public getDatabase(): Database {
    return this.getConnection().database;
  }

  /**
   * Get Collection Query
   */
  public getQuery(): Collection {
    return this.getStaticProperty("query");
  }
}
```

So our final model class will look like this:

```ts
import { Collection } from "mongodb";
import { Database } from "../database";
import connection, { Connection } from "./../connection";

export default abstract class Model {
  /**
   * Collection Name
   */
  public static collectionName = "";

  /**
   * Database Connection
   */
  public static connection: Connection = connection;

  /**
   * Get Collection query
   */
  public static query(): Collection {
    return this.connection.database.collection(this.collectionName);
  }

  /**
   * Get Collection Name
   */
  public getCollectionName(): string {
    return this.getStaticProperty("collectionName");
  }

  /**
   * Get database connection
   */
  public getConnection(): Connection {
    return this.getStaticProperty("connection");
  }

  /**
   * Get database instance
   */
  public getDatabase(): Database {
    return this.getConnection().database;
  }

  /**
   * Get Collection Query
   */
  public getQuery(): Collection {
    return this.getStaticProperty("query");
  }

  /**
   * Get Static Property
   */
  protected getStaticProperty<T>(property: keyof typeof Model): T {
    return (this.constructor as any)[property];
  }
}
```

Let's give it a try

```ts
import User from 'app/users/models/user';

// get the collection query
const user = new User;

// get the collection name
user.getCollectionName();

// get the database connection
user.getConnection();

// get the database instance
user.getDatabase();

// get the collection query
user.getQuery();
```

We can split it into 4 parts:

- Public Static Variables/Properties: to define the collection name and the connection.
- Public Static Methods: to define the collection query.
- Public Methods: to get the collection name, connection, database and collection query.
- Protected Methods: to get the static property of the child class.

The protected method is intended to not be used outside the model or one of its children as its an internal operation.

## 🎨 Conclusion

We've learned couple of good things here today, how and why to use static and non static methods, how to access static properties from a child class in the parent class, and how to use the `this.constructor` feature.

Then we created base model that allows us to get the collection query of the current model's collection name.

Then we added another feature to access the collection query or any other static methods when we create a new instance of the model.

## 🎨 Project Repository

You can find the latest updates of this project on [Github](https://github.com/hassanzohdy/nodejs-2023)

## 😍 Join our community

Join our community on [Discord](https://discord.gg/pb2vmdfhGf) to get help and support (Node Js 2023 Channel).

## 🎞️ Video Course (Arabic Voice)

If you want to learn this course in video format, you can find it on [Youtube](https://www.youtube.com/playlist?list=PLGO8ntvxgiZMJc7RN2lIq9WmMOlWZGzmz), the course is in Arabic language.

## 💰 Bonus Content 💰

You may have a look at these articles, it will definitely boost your knowledge and productivity.

General Topics

- [Event Driven Architecture: A Practical Guide in Javascript](https://dev.to/hassanzohdy/event-driven-architecture-the-best-paradigm-that-i-love-to-work-with-in-javascript-and-node-js-1gnk)
- [Best Practices For Case Styles: Camel, Pascal, Snake, and Kebab Case In Node And Javascript](https://dev.to/hassanzohdy/best-practices-for-case-styles-camel-pascal-snake-and-kebab-case-in-node-and-javascript-55oi)
- [After 6 years of practicing MongoDB, Here are my thoughts on MongoDB vs MySQL
](https://dev.to/hassanzohdy/after-6-years-of-practicing-mongodb-here-are-my-thoughts-on-mongodb-vs-mysql-574b)

Packages & Libraries

- [Collections: Your ultimate Javascript Arrays Manager](https://dev.to/hassanzohdy/collections-your-ultimate-javascript-array-handler-3o15)
- [Supportive Is: an elegant utility to check types of values in JavaScript](https://dev.to/hassanzohdy/supportive-is-an-elegant-utility-to-check-types-of-values-in-javascript-1b3e)
- [Localization: An agnostic i18n package to manage localization in your project](https://dev.to/hassanzohdy/mongez-localization-the-simplest-way-to-translate-your-website-regardless-your-favorite-framework-4gi3)

 React Js Packages

- [useFetcher: easiest way to fetch data in React Js](https://dev.to/hassanzohdy/usefetcher-the-easiest-way-to-fetch-data-in-react-45o9)

Courses (Articles)

- [React Js: Let"s Create File Manager With React Js and Node Js](https://dev.to/hassanzohdy/lets-create-a-file-manager-from-scratch-with-react-and-typescript-chapter-i-a-good-way-to-expand-your-experience-5g4k)