32-Nodejs Course 2023: Database Models: Crud Operations
Tags: node, typescript, mongodb, fastify
Published: 2022-11-03 12:52:17

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

We have successfully created a our base model and connected it to database, we also made a base structure that we'll count on in this article to start create our base crud operations.

## CRUD Operations

CRUD is an acronym for Create, Read, Update and Delete. These are the four basic functions of persistent storage.

These operations will be all static methods so we don't need to get an instance of the model to use them as the model will be used when there is a document inside it.

For example, `User.create` method will create a new user in the database and return a new instance of the user model with the data from the database.

Our base model will have the following methods:

- `create`: Create a new document in the database.
- `update`: Update a document in the database by id.
- `delete`: Delete a document in the database by id.
- `find`: Find a single document in the database by id.
- `findBy`: Find a single document in the database by a given column/field name.
- `list`: Get list of documents in the database.

Remember, these all will be static methods.

## Create

This method will receive an object of data that will be created in the database and return a new model instance with the data from the database.

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

/**
 * Base Model type that we need to define
 * to tell typescript that we're going to return a child that extends that base model
 * which will be the T (The child class)
 */
type BaseModel<T> = typeof Model & (new () => T);

export default abstract class Model {
  // ...
  
  /**
   * Create a new document in the database.
   */
  public static async create<T>(
    // this define what does this will mean here
    // T: the Type of child class
    this: BaseModel<T>,
    data: Record<string, any>,
  ): Promise<T> {
    // get the collection query
    const query = this.query();

    // insert the data
    const result = await query.insertOne(data);

    // store the data in a new object
    const modelData = { ...data };

    // get the inserted id
    modelData._id = result.insertedId;

    // now return a new model instance with the data from the database
    return new (this as any)(modelData);
  }
}
```

Wooohoo, i know there a lot of typescript stuff here, but don't worry, we'll explain it in a bit.

First we defined `BaseModel` type which we 're telling him that the `T` is the child class type that will extend the `Model` class and we'll use it to define what does `this` will mean in the `create` method.

Then we defined the `create` method as a static method that will receive a `data` object and return a new model instance with the data from the database.

Let's split it into parts:

The method will be `async`, and of course, any method that will have an operation on the database will be `async`.

It receives a an object with type `Record` which means it must be an object with any key and value.

The return type is a promise and the type of the promise is a new instance of model itself.

> typeof this.constructor which means it will return the typeof the model itself.

The workflow of the methods works as this:

- Get the collection query.
- Insert the data.
- Store the data in a new object.
- Get the inserted id.
- Return a new model instance with the data from the database.

> this.constructor as any is a hack to get the type of the model itself.

But now when we return a new instance of the model, which will be an instance of the User model, the data are passed to the constructor, but we didn't create any constructors yet, so let's do it.

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

export default abstract class Model {
  // ...
  
  /**
   * Model constructor.
   */
  public constructor(public data: Record<string, any> = {}) {
    //
  }

  /**
   * Create a new document in the database.
   */
  public static async create<T>(
    // this define what does this will mean here
    // T: the Type of child class
    this: BaseModel<T>,
    data: Record<string, any>,
  ): Promise<T> {
    // get the collection query
    const query = this.query();

    // insert the data
    const result = await query.insertOne(data);

    // store the data in a new object
    const modelData = { ...data };

    // get the inserted id
    modelData._id = result.insertedId;

    // now return a new model instance with the data from the database
    return new (this as any)(modelData);
  }
}
```

Actually we can make a protected method to return a new instnace of the model itself, so we don't need to do this every time.

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

export default abstract class Model {
  // ...
  
  /**
   * Model constructor.
   */
  public constructor(public data: Record<string, any> = {}) {
    //
  }

  /**
   * Create a new document in the database.
   */
  public static async create<T>(
    // this define what does this will mean here
    // T: the Type of child class
    this: BaseModel<T>,
    data: Record<string, any>,
  ): Promise<T> {
    // get the collection query
    const query = this.query();

    // insert the data
    const result = await query.insertOne(data);

    // store the data in a new object
    const modelData = { ...data };

    // get the inserted id
    modelData._id = result.insertedId;

    // now return a new model instance with the data from the database
    return this.self(data);
  }

  /**
   * Create a new model instance.
   */
  protected static self<T>(data: Record<string, any>): T {
    return new (this as any)(data);
  }
}
```

Now let's give it a try.

Open `src/app/users/routes.ts` file and add the following code:

```ts
// src/app/users/routes.ts
import User from "./models/user";

// we will use set time out to wait for database connection first
// this is just for testing
setTimeout(async () => {
  const user = await User.create({
    name: "Test",
  });

  console.log(user.data);
}, 4000);
```

Now if we hovered over the const `user` to see its return type it will appear like this:

![User Model](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/euo5llgpsm7ph4ijg3t9.png)

Which exactly what we were doing earlier in that complex typescript definitions.

You should see now in your terminal something like this:

![Created User](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/snohquq1xzkbc7gzqrha.png)

We'll stop at this point so you can digest what we did, and we'll continue in the next part.

## 🎨 Conclusion

In this article we learned how to work with typescript types to define the return type of same class from a static method, also we created a `create` method that receives an object and insert it into the database for any model that extends the `Model` class.

In the next part we'll continue with the `Model` class and we'll add more methods to it.

## 🎨 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)