Replace MomentJS with date-fns in Nestjs

 2 total views,  2 views today

We use NestJS at work and the backend project needs to perform date calculations and formatting in the services. Our team chose momentJS because it is the one of the most popular date-time open source project. After a while, we discovered that the library size is huge (~290kb) and it is not tree-shakable. If I want to use format function to display a date in YYYY-MM-DD format, I must import the entire library into the code.

In Angular Architect Training Course, Bonnie Brennan said moment is so huge that that we should import moment locale instead. I chose a different route and decided to get rid of moment once and for all.

After doing some research, our team decided to replace momentJS with date-fns library, a lightweight date-time library, that has all the functions our project needs.

We made the migration in five steps:

  1. Install and extend eslint-plugin-you-dont-need-momentjs plugin, run eslint on the files to find all momentJS errors
  2. Replace momentJS functions with native JS if possible
  3. Create DateFnsService service in CoreModule that encapsulates the functionality of date-fns/fp submodule
  4. Import CoreModule into other modules of the project
  5. Inject DateFnsService in constructors and replace the remaining momentJS functions with functions of DateFnsService

Step 1: Install and extend eslint-plugin-you-dont-need-momentjs plugin

npm install --save-dev eslint-plugin-you-dont-need-momentjs
"extends" : ["plugin:you-dont-need-momentjs/recommended"],

Executed npm run lint command on terminal, the plugin outputted errors and warnings.

Step 2: Convert momentJS functions to native JS

Follow the examples in https://github.com/you-dont-need/You-Dont-Need-Momentjs#parse to convert momentJS functions to native JS

For example, moment() is replaced with new Date() and isAfter is replaced with

// Before: in moment
moment('2010-10-20').isAfter('2010-10-19') => true

// After: in native JS
new Date(2010, 9, 20) > new Date(2010, 9, 19) => true

Step 3: Create DateFnsService service in CoreModule

The requirement of the service is to provide functionality to format date, and add days, months and years to a given date. It is feasible by using functions defined in date-fns/fp submodule

First, we install date-fns dependency in the project

npm install date-fns --save

Create DateFnsService service in CoreModule

 import { format, addDays, addMonths, addYears } from 'date-fns/fp'

Injectable()
export class DateFnsService {

    format(date: Date | number, format: string): string {
        return format(format)(date)
    }

    addDays(date: Date | number, amount: number): Date {
        return addDays(amount)(date)
    }

    addMonths(date: Date | number, amount: number): Date {
        return addMonths(amount)(date)
    }

    addYears(date: Date | number, amount: number): Date {
        return addYears(amount)(date)
    }
}

Export DateFnsService from CoreModule such that it can be used outside of Core

@Module({
    providers: [DateFnsService],
    exports: [DateFnsService]    
})
export class CoreModule {}

Step 4: Import CoreModule to other modules

import { CoreModule } from '@/core'

@Module({
    imports: [CoreModule],
    providers: [AppController, AppService],
    exports: []    
})
export class AppModule {}

Step 5: Use DateFnsService in place of momentJS

Inject DateFnsService in AppService’s constructor and call its functions to manipulate date inside the function

import { DateFnsService } from '@/core'
// import * as moment from 'moment'

@Inject()
export class AppService {
   constructor (private datefnsService: DateFnsService) {}

   someFunction(): string {
      // Before:  
      // return = moment()
      //    .add(1, 'days')
      //    .add(1, 'months')
      //    .add(1, 'years')
      //    .format('YYYY-MM-DD')

      // After: 
      let mydate = this.datefnsService.addDays(new Date(), 1)
      mydate = this.datefnsService.addMonths(mydate, 1)
      mydate = this.datefnsService.addYears(mydate, 1)
      return this.datefnsService.format(mydate, 'yyyy-MM-dd');  <= '2022-06-19'
   }
}

Step 5 is repeated in all services until npm run lint does not produce any momentJS error. Afterward, we remove moment dependency from package.json and the project saves around 260kb.

This is the end of the blog post! I hope you enjoy reading it and NestJS development.

Resources:

  1. https://github.com/you-dont-need/You-Dont-Need-Momentjs#days-in-month
  2. https://date-fns.org/v2.21.3/docs/fp/Getting-Started