41-Nodejs Course 2023: Working With Models data
Tags: node, typescript, mongodb, fastify
Published: 2022-11-06 04:28:20

---
title: 41-Nodejs Course 2023: Working With Models data
published: true
description: 
series: Nodejs Course 2023
tags: nodejs, typescript, mongodb, fastify
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tn6oaj53896jgpdjo32b.png
# Use a ratio of 100:42 for best results.
---

So we made such an amazing progress with the query builder and our crud model, but we still so far didn't work with the Model's concept itself, which is working the a single collection document.

## Reminder about Models

Models are the way we can work with a single document in a collection, and we can use them to modify the document's data by updating, unset-ing, replacing or even destroying the document, what differs from the `CrudModel` operations is that Crud operations works from data coming from outside, unlike the model which will perform over its internal document.

Let's see an example to illustrate it

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

// in the set time out method

const user = new User();

// now we' just created an empty model, with no data at all 

const userWithData = new User({
  name: 'hasan',
  age: 23,
  email: 'hassanzohdy@gmail.com'
});

// now we have a model with data, but it's not saved yet

// let's save it

await userWithData.save();

// we can then change the data and re-save it

userWithData.set('name', 'Ali');

await userWithData.save();
```

Now you can see what i meant by working with models internally, we can create a model with data, and then save it, and then change the data and save it again, and so on.

## Model Data And Original Data

So we are receiving the document's data in the constructor, which holds the current data of the model either by passing it from our `create` method or by creating a new model instance directly, this where we'll call it the `originalData` of the model, and we'll use it to compare the current data with the original data to know if we need to update the document or not.

## Model Class

Now let's use our `Model` class to manage the data, if you remember we set the constructor in the `BaseModel` let's move it to our model class.

```ts
// src/core/database/model/model.ts
import CrudModel from "./curd-model";
import { Document, ModelDocument } from "./types";

export default abstract class Model extends CrudModel {
  /**
   * Current data
   */
  public data: Partial<ModelDocument> = {};

  /**
   * Constructor
   */
  public constructor(public originalData: Partial<ModelDocument> = {}) {
    super();
    this.data = { ...this.originalData };
  }
}
```

What we did here we received the model data and stored it in `originalData` property, and also we assigned it to the `data` property, so we can use it to modify the data, and then we can compare it with the `originalData` to know if we need to update the document or not.

> calling `super` in the constructor is important, because we are extending the `CrudModel` class, and we need to call the constructor of the parent class, that's how inheritance works in Javascript.

## Model Methods

Now, let's think what kind of methods we can use to update the model's data.

- `set`: to set a value to a key.
- `unset`: to remove a key or more from the data.
- `replaceWith`: to replace the entire data with the given ones.
- `merge`: to merge the given data with the current data.
- `get`: to get a value from the data, or return default value if key not found.
- `has`: to check if a key exists in the data.
- `except`: to get all data except the given keys.
- `only`: to get only the given keys from the data.
- `save`: to save the data to the database.

Now let's start implementing them.

### Set

The set method receives two parameters, the key and the value, and it will set the value to the key in the data.

The key can be written in `dot.notation` syntax as well.

```ts
// src/core/database/model/model.ts
import { set } from "@mongez/reinforcements";
import CrudModel from "./curd-model";
import { Document, ModelDocument } from "./types";

export default abstract class Model extends CrudModel {
  /**
   * Current data
   */
  public data: Partial<ModelDocument> = {};

  /**
   * Constructor
   */
  public constructor(public originalData: Partial<ModelDocument> = {}) {
    super();
    this.data = { ...this.originalData };
  }
  
  /**
   * Set the value of the given key to the data list
   */
  public set(key: string, value?: any) {
    set(this.data, key, value);

    return this;
  }
}
```

We added a method called `set` it receives two parameters, the key and the value, and it will set the value to the key in the data.

We used [set](https://github.com/hassanzohdy/reinforcements#setting-value-in-object) utility to set the key deeply in the given object.

Let's see an example of usage:

```ts
import User from './models/user';

const user = new User();

user.set('name', 'hasan').set('address.city', 'Cairo');

console.log(user.data); // { name: 'hasan', address: { city: 'Cairo' } }
```

### Unset

The unset method receives one or more keys, and it will remove them from the data.

```ts
// src/core/database/model/model.ts
import { except, set } from "@mongez/reinforcements";
import CrudModel from "./curd-model";
import { Document, ModelDocument } from "./types";

export default abstract class Model extends CrudModel {
  /**
   * Current data
   */
  public data: Partial<ModelDocument> = {};

  /**
   * Constructor
   */
  public constructor(public originalData: Partial<ModelDocument> = {}) {
    super();
    this.data = { ...this.originalData };
  }
  
  /**
   * Set the value of the given key to the data list
   */
  public set(key: string, value?: any) {
    set(this.data, key, value);

    return this;
  }
  
  /**
   * Unset the given keys from the data
   */
  public unset(...keys: string[]) {
    this.data = except(this.data, keys);

    return this;
  }
}
```

We added a method called `unset` it receives one or more keys, and it will remove them from the data.

We used [except](https://github.com/hassanzohdy/reinforcements#getting-all-object-except-for-certain-keys) utility to remove the given keys from the object.

Let's see an example of usage:

```ts
import User from './models/user';

const user = new User();

user.set('name', 'hasan').set('address.city', 'Cairo');

console.log(user.data); // { name: 'hasan', address: { city: 'Cairo' } }

user.unset('name', 'address.city');

console.log(user.data); // {}
```

### Replace

The `replaceWith` method receives the new data, and it will replace the current data with the new one.

```ts
// src/core/database/model/model.ts
import { except, set } from "@mongez/reinforcements";
import CrudModel from "./curd-model";
import { Document, ModelDocument } from "./types";

export default abstract class Model extends CrudModel {
  // ..
  
  /**
   * Replace the data with the given data entirely
   */
  public replaceWith(data: Partial<ModelDocument>) {
    const newData = { ...data };

    // if the given data has no id but current model has one, then inject it in the data
    if (!newData.id && this.data.id) {
      newData.id = this.data.id;
    }

    // if the given data has no _id but current model has one, then inject it in the data
    if (!newData._id && this.data._id) {
      newData._id = this.data._id;
    }

    this.data = newData;

    return this;
  }
}
```

We added a method called `replace` it receives the new data, and it will replace the current data with the new one.

We used the spread operator to clone the given data, and then we checked if the given data has no `id` but the current model has one, then we injected it in the data, and the same for `_id`.

Let's see an example of usage:

```ts
import User from './models/user';

const user = new User();

user.set('name', 'hasan').set('address.city', 'Cairo');

console.log(user.data); // { name: 'hasan', address: { city: 'Cairo' } }

user.replace({ name: 'Hassan' });

console.log(user.data); // { name: 'Hassan' }
```

### Merge

The merge method receives the new data, and it will merge the current data with the new one.

```ts
// src/core/database/model/model.ts
import { except, merge, set } from "@mongez/reinforcements";
import CrudModel from "./curd-model";
import { Document, ModelDocument } from "./types";

export default abstract class Model extends CrudModel {
  // ..
  /**
   * Merge the data with the given object
   */
  public merge(data: Document) {
    this.data = merge(this.data, data);

    return this;
  }
}
```

We added a method called `merge` it receives the new data, and it will merge the current data with the new one.

We used [merge](https://github.com/hassanzohdy/reinforcements#merging-objects-deeply) utility to merge the given data with the current data, it will have a one additional feature than using the spread operator, it will merge the objects deeply.

Let's see an example of usage:

```ts
import User from './models/user';

const user = new User();

user.set('name', 'hasan').set('address.city', 'Cairo');

console.log(user.data); // { name: 'hasan', address: { city: 'Cairo' } }

user.merge({ name: 'Hassan', address: { country: 'Egypt' } });

console.log(user.data); // { name: 'Hassan', address: { city: 'Cairo', country: 'Egypt' } }
```

### Get

The get method receives one parameter, the key, and it will return the value of the given key.

```ts
// src/core/database/model/model.ts
import { except, get, merge, set } from "@mongez/reinforcements";
import CrudModel from "./curd-model";
import { Document, ModelDocument } from "./types";

export default abstract class Model extends CrudModel {
  // ..
  /**
   * Get the value of the given key or return default value if key does not exist
   */
  public get(key: string, defaultValue: any = null) {
    return get(this.data, key, defaultValue);
  }
}
```

We added a method called `get` it receives one parameter, the key, and it will return the value of the given key.

We used [get](https://github.com/hassanzohdy/reinforcements#getting-value-from-an-object) utility to get the value of the given key.

Let's see an example of usage:

```ts
import User from './models/user';

const user = new User();

user.set('name', 'hasan').set('address.city', 'Cairo');

console.log(user.get('name')); // 'hasan'
console.log(user.get('address.city')); // 'Cairo'
console.log(user.get('address.country', 'Egypt')); // 'Egypt'
```

### Has

The has method receives one parameter, the key, and it will return true if the given key exists in the data, otherwise it will return false.

```ts
// src/core/database/model/model.ts
import { except, get, merge, set } from "@mongez/reinforcements";
import CrudModel from "./curd-model";
import { Document, ModelDocument } from "./types";

const MISSING_KEY = Symbol("MISSING_KEY");

export default abstract class Model extends CrudModel {
  // ..
  
  /**
   * Check if the given key exists in the data
   */
  public has(key: string) {
    return this.get(key, MISSING_KEY) === MISSING_KEY;
  }
}
```

We used here something kind of new in our code base, we used a symbol to represent a missing key, and we used it to check if the given key exists in the data or not.

Let's see an example of usage:

```ts
import User from './models/user';

const user = new User();

user.set('name', 'hasan').set('address.city', 'Cairo');

console.log(user.has('name')); // true
console.log(user.has('address.country')); // false
```

### Except

The except method receives one parameter, the keys, and it will return a new object without the given keys.

```ts
// src/core/database/model/model.ts
import { except, get, merge, set } from "@mongez/reinforcements";
import CrudModel from "./curd-model";
import { Document, ModelDocument } from "./types";

const MISSING_KEY = Symbol("MISSING_KEY");

export default abstract class Model extends CrudModel {
  // ..
  /**
   * Get a new object without the given keys
   */
  public except(keys: string[]) {
    return except(this.data, keys);
  }
}
```

We added a method called `except` it receives one parameter, the keys, and it will return a new object without the given keys.

We used [except](https://github.com/hassanzohdy/reinforcements#getting-all-object-except-for-certain-keys) utility to get a new object without the given keys.

Let's see an example of usage:

```ts
import User from './models/user';

const user = new User();

user.set('name', 'hasan').set('address.city', 'Cairo');

console.log(user.except(['name'])); // { address: { city: 'Cairo' } }
```

### Only

The only method receives one parameter, the keys, and it will return a new object with only the given keys.

```ts
// src/core/database/model/model.ts
import { except, get, merge, only, set } from "@mongez/reinforcements";
import CrudModel from "./curd-model";
import { Document, ModelDocument } from "./types";

const MISSING_KEY = Symbol("MISSING_KEY");

export default abstract class Model extends CrudModel {
  // ..
  /**
   * Get a new object with only the given keys
   */
  public only(keys: string[]) {
    return only(this.data, keys);
  }
}
```

We added a method called `only` it receives one parameter, the keys, and it will return a new object with only the given keys.

We used [only](https://github.com/hassanzohdy/reinforcements#getting-certain-values-from-object) utility to get a new object with only the given keys.

Let's see an example of usage:

```ts
import User from './models/user';

const user = new User();

user.set('name', 'hasan').set('address.city', 'Cairo');

console.log(user.only(['name'])); // { name: 'hasan' }
```

## 🎨 Conclusion

In this article, we made a quick recap about the concept of Models as a model it works with only one single document, we also defined a new property called `originalData` which will hold the original data of the document, and we added some methods to the model to make it easier to work with the data.

In our next article, we'll see the save method and how it will work with the database.

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