Simple way to handle monetary data in MongoDB in a NestJs application

19 / Aug / 2023 by Shivang Chaturvedi 0 comments

While building my backend NestJs application, I wanted to store some financial data. The first question I ran into was what schema Type should be for storing data where I want to maintain the data precision.

Decimal128 came out to be the prescribed official candidate to store the financial data.

Here’s my initial schema implementation:

import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import mongoose, { Decimal128, HydratedDocument, Types } from 'mongoose';

export type UserDocument = HydratedDocument<User>;

@Schema({
  toJSON: {
    getters: true,
  },
})
export class User {
  @Prop()
  firstName: string;

  @Prop()
  lastName: string;

  @Prop({
    default: new Types.Decimal128('0'),
    type: Types.Decimal128,
  })
  total_invested: Decimal128;

  @Prop({
    default: new Types.Decimal128('0'),
    type: Types.Decimal128,
  })
  target_returns: Decimal128;

  @Prop({
    default: new Types.Decimal128('0'),
    type: Types.Decimal128,
  })
  target_profit_percent: Decimal128;

  @Prop({
    default: new Types.Decimal128('0'),
    type: Types.Decimal128,
  })
  actual_returns: Decimal128;

  @Prop({
    default: new Types.Decimal128('0'),
    type: Types.Decimal128,
  })
  actual_profit_percent: Decimal128;
}

export const UserSchema = SchemaFactory.createForClass(User);

There was one small issue that I soon ran into. The database would return the Decimal128 column data in this format:

{
    "_id": "64d50a0bf9e3e104920dec6a",
    "firstName": "shiva",
    "lastName": "chaturvedi",
    "total_invested": {
        "$numberDecimal": "0"
    },
    "target_returns": {
        "$numberDecimal": "0"
    },
    "target_profit_percent": {
        "$numberDecimal": "0"
    },
    "actual_returns": {
        "$numberDecimal": "0"
    },
    "actual_profit_percent": {
        "$numberDecimal": "0"
    },
    "__v": 0,
    "id": "64d50a0bf9e3e104920dec6a"
}

Whereas I wanted the database to return transformed results or, simply put, data in a good old-fashioned key-value pair format.

Solution: Use Getters

Implementing getters your schema props is very easy.

import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import mongoose, { Decimal128, HydratedDocument, Types } from 'mongoose';

export type UserDocument = HydratedDocument<User>;

@Schema()
export class User {
  @Prop()
  firstName: string;

  @Prop()
  lastName: string;

  @Prop({
    default: new Types.Decimal128('0'),
    type: Types.Decimal128,
    get: (v: Types.Decimal128) => v.toString(),
  })
  total_invested: Decimal128;

  @Prop({
    default: new Types.Decimal128('0'),
    type: Types.Decimal128,
    get: (v: Types.Decimal128) => v.toString(),
  })
  target_returns: Decimal128;

  @Prop({
    default: new Types.Decimal128('0'),
    type: Types.Decimal128,
    get: (v: Types.Decimal128) => v.toString(),
  })
  target_profit_percent: Decimal128;

  @Prop({
    default: new Types.Decimal128('0'),
    type: Types.Decimal128,
    get: (v: Types.Decimal128) => v.toString(),
  })
  actual_returns: Decimal128;

  @Prop({
    default: new Types.Decimal128('0'),
    type: Types.Decimal128,
    get: (v: Types.Decimal128) => v.toString(),
  })
  actual_profit_percent: Decimal128;
}

export const UserSchema = SchemaFactory.createForClass(User);

Now, let’s test the API again.

“What am I missing?” I wondered.

After a short walk on the internet, a good samaritan pointed out that we also need to set getters flag as true on the @Schema decorator as illustrated below:

import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import mongoose, { Decimal128, HydratedDocument, Types } from 'mongoose';

export type UserDocument = HydratedDocument<User>;

@Schema({
  toJSON: {
    getters: true,
  },
})

This enables the props to execute/use their getter functions.

Let’s test again!

Hallelujah ? ?

Want to know more about Decimal128 Type? Read this beautiful article written by Ken W. Alger on MongoDB official website.

P.S.: If you feel something can be improved or lacks proper explanation, drop us a note in the comment section below, and we’ll try to

FOUND THIS USEFUL? SHARE IT

Tag -

MongoDB NestJs

Leave a Reply

Your email address will not be published. Required fields are marked *