<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Dawit Girma</title>
    <description>The latest articles on Forem by Dawit Girma (@dedawit).</description>
    <link>https://forem.com/dedawit</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2156145%2Ffa461de3-34f9-4d49-a837-e5a23a24016b.jpg</url>
      <title>Forem: Dawit Girma</title>
      <link>https://forem.com/dedawit</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/dedawit"/>
    <language>en</language>
    <item>
      <title>One Codebase, Multiple Environments: Mastering Env Config in NestJS</title>
      <dc:creator>Dawit Girma</dc:creator>
      <pubDate>Thu, 16 Apr 2026 16:46:32 +0000</pubDate>
      <link>https://forem.com/dedawit/one-codebase-multiple-environments-mastering-env-config-in-nestjs-3ebk</link>
      <guid>https://forem.com/dedawit/one-codebase-multiple-environments-mastering-env-config-in-nestjs-3ebk</guid>
      <description>&lt;p&gt;Have you ever wondered:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;“Can I tell NestJS which environment variables to load when running my app?”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The simple answer is: yes, you absolutely can.&lt;/p&gt;

&lt;p&gt;This is not one of my major deep-dive articles, but I wanted to share it because it used to be a real headache for me—and it might be for you as well.&lt;/p&gt;

&lt;p&gt;At our company, we constantly switch between local, development, and production databases. What we used to do was far from ideal:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Comment one .env file&lt;/li&gt;
&lt;li&gt;Uncomment another&lt;/li&gt;
&lt;li&gt;Repeat the same process again and again&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It worked, but it was messy, error-prone, and frustrating.&lt;br&gt;
Until I found a cleaner and more scalable approach.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 1: Install Required Packages
&lt;/h2&gt;

&lt;p&gt;We need two packages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;@nestjs/config for loading environment variables&lt;/li&gt;
&lt;li&gt;cross-env for handling NODE_ENV across platforms
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @nestjs/config cross-env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Step 2: Create Multiple .env Files
&lt;/h2&gt;

&lt;p&gt;Create environment-specific files such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;.env.local &lt;/li&gt;
&lt;li&gt;.env.test &lt;/li&gt;
&lt;li&gt;.env.production&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can create as many as needed depending on your workflow.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 3: Define Scripts in &lt;strong&gt;package.json&lt;/strong&gt;
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"start:test"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cross-env NODE_ENV=test nest start --watch"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"start:local"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cross-env NODE_ENV=local nest start --watch"&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The key idea:&lt;/p&gt;

&lt;p&gt;The value of &lt;strong&gt;NODE_ENV&lt;/strong&gt; must match the suffix of your &lt;strong&gt;.env&lt;/strong&gt; file.&lt;br&gt;
For example:&lt;br&gt;
&lt;strong&gt;NODE_ENV&lt;/strong&gt;=test loads &lt;strong&gt;.env.test&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;NODE_ENV&lt;/strong&gt;=local loads &lt;strong&gt;.env.local&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 4: Configure NestJS
&lt;/h2&gt;

&lt;p&gt;In your app.module.ts, add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;ConfigModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forRoot&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
 &lt;span class="na"&gt;envFilePath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`.env.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODE_ENV&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="na"&gt;isGlobal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}),&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tells NestJS to dynamically load the correct environment file based on &lt;strong&gt;NODE_ENV&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Step 5: Run Your Application
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can run your app in different environments easily:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run start:test 
npm run start:local
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or any other environment you define:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run start:other
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;What used to be a repetitive and error-prone process is now clean and predictable.&lt;/p&gt;

&lt;p&gt;No more commenting and uncommenting configuration files.&lt;/p&gt;

&lt;p&gt;No more accidental connections to the wrong database.&lt;/p&gt;

&lt;p&gt;Just one codebase running across multiple environments in a controlled way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Contact
&lt;/h2&gt;

&lt;p&gt;If you have any questions, feel free to reach out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;LinkedIn: &lt;a href="https://linkedin.com/in/dawit-girma-7b8867228/" rel="noopener noreferrer"&gt;https://linkedin.com/in/dawit-girma-7b8867228/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Email: &lt;a href="mailto:realdavis7779@gmail.com"&gt;realdavis7779@gmail.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/dedawit" rel="noopener noreferrer"&gt;https://github.com/dedawit&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>backend</category>
      <category>node</category>
      <category>tutorial</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Mastering Cron Jobs in NestJS: A Complete Guide with Real Examples (and Related Scheduling Mechanisms)</title>
      <dc:creator>Dawit Girma</dc:creator>
      <pubDate>Sat, 11 Apr 2026 13:47:58 +0000</pubDate>
      <link>https://forem.com/dedawit/mastering-cron-jobs-in-nestjs-a-complete-guide-with-real-examples-and-related-scheduling-546a</link>
      <guid>https://forem.com/dedawit/mastering-cron-jobs-in-nestjs-a-complete-guide-with-real-examples-and-related-scheduling-546a</guid>
      <description>&lt;ul&gt;
&lt;li&gt;How do you automatically send daily reports to users without manual intervention?&lt;/li&gt;
&lt;li&gt; How can a system periodically clean up expired data such as sessions or tokens?&lt;/li&gt;
&lt;li&gt; What is the most reliable way to execute a task every midnight?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For these types of problems, &lt;strong&gt;cron jobs&lt;/strong&gt;  provide an effective and scalable solution.&lt;/p&gt;

&lt;p&gt;Cron jobs are a scheduling mechanism used to automate repetitive tasks in software systems by executing code at predefined times or intervals. Instead of relying on manual triggers or user actions, cron jobs enable applications to handle background operations such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sending emails&lt;/li&gt;
&lt;li&gt;Cleaning expired data&lt;/li&gt;
&lt;li&gt;Generating reports&lt;/li&gt;
&lt;li&gt;Synchronizing with external services&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach improves efficiency, minimizes human error, and ensures that critical tasks run consistently and on time.&lt;/p&gt;

&lt;p&gt;In modern backend development, frameworks like NestJS provide built-in support for task scheduling, allowing developers to implement cron-based automation in a clean, structured, and scalable way.&lt;/p&gt;

&lt;p&gt;This article explores how to use cron jobs in NestJS, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Declarative scheduling&lt;/li&gt;
&lt;li&gt;Dynamic scheduling&lt;/li&gt;
&lt;li&gt;Interval-based execution&lt;/li&gt;
&lt;li&gt;Timeout-based execution&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Basic knowledge of NestJS&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Project Setup
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Create a New NestJS Application
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nest new nestjs-cron
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Install Scheduling Dependency
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install --save @nestjs/schedule
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Configure Schedule Module&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Update your app.module.ts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ScheduleModule } from '@nestjs/schedule';

@Module({
 imports: [ScheduleModule.forRoot()],
 controllers: [AppController],
 providers: [AppService],
})
export class AppModule {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we are all done with the setup, so we can create declarative and dynamic cron jobs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Declarative Cron Jobs
&lt;/h2&gt;

&lt;p&gt;Declarative cron jobs are defined before the application starts and run automatically based on a schedule.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cron Expression Format
&lt;/h2&gt;

&lt;p&gt;A cron expression typically consists of 6 fields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;*  *  *  *  *  *
|  |  |  |  |  |
|  |  |  |  |  └── Day of Week (0 - 7) (Sunday = 0 or 7)
|  |  |  |  └───── Month (1 - 12)
|  |  |  └──────── Day of Month (1 - 31)
|  |  └─────────── Hour (0 - 23)
|  └────────────── Minute (0 - 59)
└───────────────── Second (0 - 59)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Note: The seconds field is optional in some implementations.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The asterisk (*) represents “every” value.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;* in the minute field → every minute&lt;/li&gt;
&lt;li&gt;* * * * * → runs every minute&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If interpreting cron expressions becomes challenging, you can use online tools such as &lt;a href="https://dev.tourl"&gt;crontab.guru&lt;/a&gt; to simplify the process. This tool allows you to input or modify cron expression values and instantly displays a human-readable explanation of the schedule. By adjusting the fields, you can quickly understand how each change affects execution timing. An illustrative example is shown below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6p3lyz2vb2shq23itfcw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6p3lyz2vb2shq23itfcw.png" alt=" " width="800" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;NestJS also provides predefined expressions for readability using CronExpression enum. This approach is easier to understand but less flexible than custom expressions. E.g.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CronExpression.EVERY_10_MINUTES
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Below is example service created and imported in app.module.ts to illustrate.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Injectable, Logger } from '@nestjs/common';
import { Cron, CronExpression } from '@nestjs/schedule';


@Injectable()
export class TestCronService {
 private readonly logger = new Logger(TestCronService.name);


 // Runs every minute using asterisk expression
 @Cron('* * * * *', {
   name: 'asterisk-every-minute',
   timeZone: 'Africa/Addis_Ababa',
 })
 handleAsteriskEveryMinute() {
   this.logger.log('Running cron: asterisk-every-minute');
 }


 // Runs every minute using CronExpression enum
 @Cron(CronExpression.EVERY_MINUTE, {
   name: 'enum-every-minute',
   timeZone: 'Africa/Addis_Ababa',
 })
 handleEnumEveryMinute() {
   this.logger.log('Running cron: enum-every-minute');
 }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the provider section of app.module.ts should look like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; providers: [AppService, TestCronService],
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And finally run your program, you should see logs of the above two cron jobs. Like&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd8gn179yomsilw50frhd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd8gn179yomsilw50frhd.png" alt=" " width="800" height="129"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Declarative Intervals
&lt;/h2&gt;

&lt;p&gt;Intervals allow repeated execution at fixed time intervals using &lt;strong&gt;setInterval()&lt;/strong&gt; internally. Example code and log image is provided below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; // Runs every 2 seconds using declarative interval
 @Interval('every-2-seconds', 2000)
 handleEvery2Seconds() {
   this.logger.log('declarative interval every 2 seconds (2000ms)');
 }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdm3b0verfmu99vrfd44z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdm3b0verfmu99vrfd44z.png" alt=" " width="800" height="164"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Declarative Timeout
&lt;/h2&gt;

&lt;p&gt;Timeouts execute a task only once after a specified delay. Internally, they rely on JavaScript’s &lt;strong&gt;setTimeout()&lt;/strong&gt; function to schedule the execution.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; // Runs once after 5 seconds using declarative timeout
 @Timeout('after-5-seconds', 5000)
 handleAfter5Seconds() {
   this.logger.log('declarative timeout after 5 seconds (5000ms)');
 }


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7pxi4q8mypgnor8o5op4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7pxi4q8mypgnor8o5op4.png" alt=" " width="800" height="164"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Dynamic cron Jobs
&lt;/h2&gt;

&lt;p&gt;Dynamic cron jobs enable runtime management of scheduled tasks, allowing you to create, retrieve, update, or delete cron jobs while the application is running. To perform these operations, NestJS provides the &lt;strong&gt;SchedulerRegistry API&lt;/strong&gt;, which must be injected into your service to gain programmatic control over the scheduling system.&lt;/p&gt;

&lt;p&gt;Key methods&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;addCronJob() – register a job&lt;/li&gt;
&lt;li&gt;getCronJob() – retrieve a job&lt;/li&gt;
&lt;li&gt;deleteCronJob() – remove a job&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cron job controls after we get cron job by name&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;stop() – stop execution&lt;/li&gt;
&lt;li&gt;start() – restart job&lt;/li&gt;
&lt;li&gt;setTime() – update schedule&lt;/li&gt;
&lt;li&gt;lastDate() – last execution time&lt;/li&gt;
&lt;li&gt;nextDate() – next execution time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example code is given below with its log.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Injectable, Logger } from '@nestjs/common';
import { SchedulerRegistry, Timeout } from '@nestjs/schedule';
import { CronJob, CronTime } from 'cron';
import { DateTime } from 'luxon';


@Injectable()
export class DynamicCronService {
 private readonly logger = new Logger(DynamicCronService.name);


 constructor(private readonly schedulerRegistry: SchedulerRegistry) {}


 // Creates dynamic-job after 10 seconds and demonstrates all scheduler methods
 @Timeout('create-dynamic-cron', 10000)
 async createDynamicCron() {
   const job = new CronJob('* * * * * *', () =&amp;gt; {
     this.logger.log('[dynamic-job] tick');
   });


   this.schedulerRegistry.addCronJob('dynamic-job', job);
   job.start();
   this.logger.log('dynamic-job created and started (every second)');


   const cronJob = this.schedulerRegistry.getCronJob('dynamic-job');


   // stop() - stops the running cron job
   cronJob.stop();
   this.logger.log('stop()     — dynamic-job stopped');


   // start() - restarts the stopped cron job
   cronJob.start();
   this.logger.log('start()    — dynamic-job restarted');


   // setTime() - stops the job and restarts it with a new schedule (every 5 seconds)
   cronJob.setTime(new CronTime('*/5 * * * * *'));
   cronJob.start();
   this.logger.log(
     'setTime()  — schedule changed to every 5 seconds, job restarted',
   );


   // wait 5 seconds so the job executes at least once before checking lastDate()
   await new Promise((resolve) =&amp;gt; setTimeout(resolve, 5000));


   // lastDate() - returns last DateTime of execution (null if not yet executed)
   this.logger.log(
     `lastDate() — last execution: ${cronJob.lastDate() ? DateTime.fromJSDate(cronJob.lastDate()!) : 'not yet executed'}`,
   );


   // nextDate() - returns next DateTime of execution as a luxon DateTime
   this.logger.log(`nextDate() — next execution: ${cronJob.nextDate()}`);
 }


 // Stops and deletes dynamic-job after 30 seconds
 @Timeout('delete-dynamic-cron', 30000)
 deleteDynamicCron() {
   const cronJob = this.schedulerRegistry.getCronJob('dynamic-job');
   cronJob.stop();
   this.schedulerRegistry.deleteCronJob('dynamic-job');
   this.logger.log('dynamic-job stopped and deleted after 30 seconds');
 }
}




&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy6x0hpwbagilstnw7s1t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy6x0hpwbagilstnw7s1t.png" alt=" " width="800" height="233"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Dynamic Intervals
&lt;/h2&gt;

&lt;p&gt;Intervals can be managed at runtime using the &lt;strong&gt;SchedulerRegistry API&lt;/strong&gt;, allowing you to create, retrieve, and delete named intervals dynamically. For clarity, the previous provider can be replaced with a dedicated dynamic interval service, which should then be registered in app.module.ts.&lt;/p&gt;

&lt;p&gt;The following example demonstrates the complete lifecycle of a dynamic interval: an interval is created after 5 seconds, retrieved for reference, and automatically removed after running for 20 seconds.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
import { Injectable, Logger } from '@nestjs/common';
import { SchedulerRegistry, Timeout } from '@nestjs/schedule';


@Injectable()
export class DynamicIntervalService {
 private readonly logger = new Logger(DynamicIntervalService.name);


 constructor(private readonly schedulerRegistry: SchedulerRegistry) {}


 // Creates dynamic-interval after 5 seconds and registers it with SchedulerRegistry
 @Timeout('create-dynamic-interval', 5000)
 createDynamicInterval() {
   const interval = setInterval(() =&amp;gt; {
     this.logger.log('[dynamic-interval] tick every 2 seconds');
   }, 2000);


   this.schedulerRegistry.addInterval('dynamic-interval', interval);
   this.logger.log('dynamic-interval created and started (every 2 seconds)');


   // getInterval() - retrieves the registered interval reference
   const ref = this.schedulerRegistry.getInterval('dynamic-interval');
   this.logger.log(`getInterval() — interval ref retrieved, id: ${ref}`);
 }


 // Deletes dynamic-interval after 20 seconds
 @Timeout('delete-dynamic-interval', 20000)
 deleteDynamicInterval() {
   this.schedulerRegistry.deleteInterval('dynamic-interval');
   this.logger.log('dynamic-interval deleted after 20 seconds');
 }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcje0amrz15cjltg65osp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcje0amrz15cjltg65osp.png" alt=" " width="800" height="220"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Dynamic Timeouts
&lt;/h2&gt;

&lt;p&gt;Timeouts can also be managed dynamically at runtime using the &lt;strong&gt;SchedulerRegistry API&lt;/strong&gt;, which allows you to create, retrieve, and delete timeout tasks programmatically. For clarity, you can replace the previous provider with a dedicated dynamic timeout service and register it in app.module.ts.&lt;/p&gt;

&lt;p&gt;The following example illustrates the full lifecycle of a dynamic timeout: a timeout is created after 5 seconds, its reference is retrieved for inspection, and it is subsequently deleted after 20 seconds.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
import { Injectable, Logger } from '@nestjs/common';
import { SchedulerRegistry, Timeout } from '@nestjs/schedule';


@Injectable()
export class DynamicTimeoutService {
 private readonly logger = new Logger(DynamicTimeoutService.name);


 constructor(private readonly schedulerRegistry: SchedulerRegistry) {}


 // Creates dynamic-timeout after 5 seconds and registers it with SchedulerRegistry
 @Timeout('create-dynamic-timeout', 5000)
 createDynamicTimeout() {
   const timeout = setTimeout(() =&amp;gt; {
     this.logger.log('[dynamic-timeout] fired after 10 seconds');
   }, 10000);


   this.schedulerRegistry.addTimeout('dynamic-timeout', timeout);
   this.logger.log(
     'dynamic-timeout created and registered (fires in 10 seconds)',
   );


   // getTimeout() - retrieves the registered timeout reference
   const ref = this.schedulerRegistry.getTimeout('dynamic-timeout');
   this.logger.log(`getTimeout() — timeout ref retrieved, id: ${ref}`);
 }


 // Deletes dynamic-timeout after 20 seconds (before it fires)
 @Timeout('delete-dynamic-timeout', 20000)
 deleteDynamicTimeout() {
   this.schedulerRegistry.deleteTimeout('dynamic-timeout');
   this.logger.log('dynamic-timeout deleted after 20 seconds ');
 }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkio815wddf61kvlrhz03.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkio815wddf61kvlrhz03.png" alt=" " width="800" height="148"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;NestJS provides a powerful and flexible scheduling system through @nestjs/schedule, enabling developers to implement:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cron-based automation&lt;/li&gt;
&lt;li&gt;Interval-based execution&lt;/li&gt;
&lt;li&gt;Timeout-based execution&lt;/li&gt;
&lt;li&gt;Runtime (dynamic) scheduling control&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By leveraging these features, you can build reliable backend systems that automate repetitive tasks efficiently and maintain consistent system behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  Contact
&lt;/h2&gt;

&lt;p&gt;If you have any questions, feel free to reach out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;LinkedIn: &lt;a href="https://linkedin.com/in/dawit-girma-7b8867228/" rel="noopener noreferrer"&gt;https://linkedin.com/in/dawit-girma-7b8867228/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Email: &lt;a href="mailto:realdavis7779@gmail.com"&gt;realdavis7779@gmail.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Github: &lt;a href="https://github.com/dedawit" rel="noopener noreferrer"&gt;https://github.com/dedawit&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The final code is available at public repository:&lt;br&gt;
&lt;a href="https://github.com/dedawit/nestjs-cron.git" rel="noopener noreferrer"&gt;https://github.com/dedawit/nestjs-cron.git&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://dev.to/jay818/cron-jobs-in-nest-js-1g43"&gt;https://dev.to/jay818/cron-jobs-in-nest-js-1g43&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/mohinsheikh/cron-jobs-a-comprehensive-guide-from-basics-to-advanced-usage-2p40"&gt;https://dev.to/mohinsheikh/cron-jobs-a-comprehensive-guide-from-basics-to-advanced-usage-2p40&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.nestjs.com/techniques/task-scheduling" rel="noopener noreferrer"&gt;https://docs.nestjs.com/techniques/task-scheduling&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>nestjs</category>
      <category>cronjobs</category>
      <category>backend</category>
      <category>automation</category>
    </item>
    <item>
      <title>Advanced Postman Concepts for Efficient API Testing</title>
      <dc:creator>Dawit Girma</dc:creator>
      <pubDate>Thu, 12 Mar 2026 18:42:53 +0000</pubDate>
      <link>https://forem.com/dedawit/advanced-postman-concepts-for-efficient-api-testing-6kh</link>
      <guid>https://forem.com/dedawit/advanced-postman-concepts-for-efficient-api-testing-6kh</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Postman is a widely used API client that enables developers to test APIs and verify responses efficiently. While many developers use Postman for basic API requests, the platform offers a wide range of advanced features that can significantly simplify and accelerate the API testing process.&lt;/p&gt;

&lt;p&gt;In this article, we will explore several advanced Postman techniques that help streamline API testing and improve productivity. These features include dynamic URLs, token-based authentication using variables, sending files in request bodies, using randomly generated variables, and managing path and query parameters effectively.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before reading this article, it is recommended that you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Basic knowledge of APIs&lt;/li&gt;
&lt;li&gt;Basic familiarity with Postman&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Advanced Features Covered
&lt;/h2&gt;

&lt;p&gt;In this article, we will explore the following advanced Postman features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dynamic URLs using environments&lt;/li&gt;
&lt;li&gt;Token-based authentication using variables&lt;/li&gt;
&lt;li&gt;Sending files in request bodies&lt;/li&gt;
&lt;li&gt;Randomly generated variables for test data&lt;/li&gt;
&lt;li&gt;Path parameters&lt;/li&gt;
&lt;li&gt;Query parameters&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each of these features helps make API testing more flexible, maintainable, and efficient.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dynamic URLs Using Environments
&lt;/h2&gt;

&lt;p&gt;When testing APIs, many developers paste the full URL directly into the request input field. However, this approach is not recommended. The best practice is to store the &lt;strong&gt;base URL&lt;/strong&gt; in a Postman environment variable and reference it within requests.&lt;/p&gt;

&lt;p&gt;This approach is particularly useful when switching between environments, such as local development, staging, or production. Instead of modifying every request URL, you only need to update the base URL in the environment.&lt;/p&gt;

&lt;p&gt;Steps to Create an Environment Variable&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In Postman, go to the &lt;strong&gt;top left section of the sidebar.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb7zanz9xbry3vpm7nzpu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb7zanz9xbry3vpm7nzpu.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click the plus (+) icon.&lt;/li&gt;
&lt;li&gt;Select Environment.&lt;/li&gt;
&lt;li&gt;Create a new environment.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You will see a table where you can define variable–value pairs.&lt;/p&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm53jgqo60oedofhfp36e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm53jgqo60oedofhfp36e.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once this variable is defined, instead of writing the full URL such as:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;http://localhost:3003/auth/login&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;you can write:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;{{BASE_URL}}/auth/login&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwpmkruxe6byvvgv88tas.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwpmkruxe6byvvgv88tas.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, if the backend server changes (for example switching from local development to a remote server), you only need to update the BASE_URL variable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Token-Based Authentication Using Variables
&lt;/h2&gt;

&lt;p&gt;Many developers manually copy the access token from the login response and paste it into every authenticated request. This approach is inefficient and prone to errors.&lt;/p&gt;

&lt;p&gt;A better approach is to &lt;strong&gt;store the access token in an environment variable automatically after login.&lt;/strong&gt; Once stored, it can be reused in all authenticated requests.&lt;/p&gt;

&lt;p&gt;Steps&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the &lt;strong&gt;Scripts&lt;/strong&gt; section in the Postman request page.&lt;/li&gt;
&lt;li&gt;Add a script to extract the token from the response.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1p0bfq1fkh07ez9hce6x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1p0bfq1fkh07ez9hce6x.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;p&gt;pm.environment.set("ACCESS_TOKEN", pm.response.json().accessToken);&lt;/p&gt;

&lt;p&gt;This script saves the access token into the environment variable &lt;strong&gt;ACCESS_TOKEN.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you work with multiple roles (for example, admin and user), you can store multiple tokens:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ACCESS_TOKEN_ADMIN&lt;/li&gt;
&lt;li&gt;ACCESS_TOKEN_USER&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using the Token in Requests&lt;/p&gt;

&lt;p&gt;In the &lt;strong&gt;Authorization&lt;/strong&gt; section of your request:&lt;/p&gt;

&lt;p&gt;Choose &lt;strong&gt;Bearer Token&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use the variable:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;{{ACCESS_TOKEN_ADMIN}}&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftbv7iwr72a5j0qxd0b4x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftbv7iwr72a5j0qxd0b4x.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, whenever you log in again and receive a new token, the variable updates automatically. There is no need to manually copy and paste tokens.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sending Files in the Request Body
&lt;/h2&gt;

&lt;p&gt;Files cannot be sent as part of a JSON request body in the &lt;strong&gt;raw&lt;/strong&gt; section. Instead, Postman provides support for file uploads using &lt;strong&gt;form-data.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Steps to Send Files&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to the &lt;strong&gt;Body&lt;/strong&gt; tab.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;form-data.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgl2govosfvxylgfu0qxo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgl2govosfvxylgfu0qxo.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You will see two columns:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Key&lt;/li&gt;
&lt;li&gt;Value&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In form-data, values can be either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Text&lt;/li&gt;
&lt;li&gt;File&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is a small dropdown selector in the &lt;strong&gt;key column&lt;/strong&gt; where you can switch between &lt;strong&gt;Text&lt;/strong&gt; and &lt;strong&gt;File&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you choose &lt;strong&gt;File&lt;/strong&gt;, Postman will allow you to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Upload a file from your system&lt;/li&gt;
&lt;li&gt;Select a file from Postman Cloud&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, a request body might contain:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftkq924vicpf7y2yvqzpo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftkq924vicpf7y2yvqzpo.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This format allows the backend API to receive both files and additional fields in the same request.&lt;/p&gt;

&lt;h2&gt;
  
  
  Randomly Generated Variables for Request Data
&lt;/h2&gt;

&lt;p&gt;During API testing, developers often reuse the same data repeatedly, such as the same name, email, or city. This can create problems when testing systems that require unique values.&lt;/p&gt;

&lt;p&gt;Postman provides &lt;strong&gt;dynamic variables&lt;/strong&gt; that automatically generate random data for each request.&lt;/p&gt;

&lt;p&gt;Examples include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;$randomPhoneNumber&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;$randomIP&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;$randomBankAccount&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;$randomEmail&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;$randomFirstName&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example request body:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F903z2wdqs7g8y3w3u2uu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F903z2wdqs7g8y3w3u2uu.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Using dynamic variables helps generate unique test data automatically, making it easier to test user creation, registrations, and other API operations.&lt;/p&gt;

&lt;p&gt;You can find a full list of dynamic variables in the Postman documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Path Parameters
&lt;/h2&gt;

&lt;p&gt;Many APIs use &lt;strong&gt;path parameters&lt;/strong&gt;, typically for resource identifiers such as IDs.&lt;/p&gt;

&lt;p&gt;A common approach is to hardcode the ID directly in the URL. However, this makes testing less flexible.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;/users/fac9a13a-3b01-4052-9965-fb2479b3150e&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Instead, you can define the parameter using a variable - using colon and name of variable:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;/users/:id&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffyjm93g8hf3obl216ms7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffyjm93g8hf3obl216ms7.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Postman will automatically create an input field in the &lt;strong&gt;Path Variables&lt;/strong&gt; section where you can enter the value of id.&lt;/p&gt;

&lt;p&gt;This makes it easy to test different resource IDs without modifying the request URL manually.&lt;/p&gt;

&lt;h2&gt;
  
  
  Query Parameters
&lt;/h2&gt;

&lt;p&gt;Query parameters are commonly added manually in the URL using a question mark (?) followed by key–value pairs.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;/users?role=admin&amp;amp;page=2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;However, manually editing URLs can become difficult when multiple parameters are involved.&lt;/p&gt;

&lt;p&gt;Postman provides a &lt;strong&gt;Query Params&lt;/strong&gt; section that allows you to define query parameters using key–value pairs.&lt;/p&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fclai0l89eancq4us9any.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fclai0l89eancq4us9any.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Postman automatically constructs the request URL for you. This approach improves readability and makes parameter management easier.&lt;/p&gt;
&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Postman provides powerful tools that go beyond simple API request testing. By using advanced features such as environment variables, automated token management, dynamic variables, and structured request parameters, developers can significantly improve the efficiency and maintainability of their API testing workflows.&lt;/p&gt;

&lt;p&gt;In this article, we explored several practical techniques that simplify API testing, particularly for backend developers working with authentication, dynamic data, and flexible request configurations.&lt;/p&gt;

&lt;p&gt;Mastering these features can greatly enhance your development and debugging process.&lt;/p&gt;
&lt;h2&gt;
  
  
  Contact
&lt;/h2&gt;

&lt;p&gt;If you have any questions, feel free to reach out:&lt;/p&gt;

&lt;p&gt;LinkedIn:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://linkedin.com/in/dawit-girma-7b8867228/" rel="noopener noreferrer"&gt;https://linkedin.com/in/dawit-girma-7b8867228/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Email:&lt;/p&gt;

&lt;p&gt;&lt;a href="mailto:realdavis7779@gmail.com"&gt;realdavis7779@gmail.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;GitHub:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/dedawit" rel="noopener noreferrer"&gt;https://github.com/dedawit&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Reference:
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;

&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://learning.postman.com/docs/introduction/overview" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;learning.postman.com&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;




&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>backend</category>
      <category>postman</category>
      <category>postmanapi</category>
      <category>testing</category>
    </item>
    <item>
      <title>Entity-Attribute-Value (EAV) Model</title>
      <dc:creator>Dawit Girma</dc:creator>
      <pubDate>Tue, 10 Feb 2026 20:17:02 +0000</pubDate>
      <link>https://forem.com/dedawit/entity-attribute-value-eav-model-2ghg</link>
      <guid>https://forem.com/dedawit/entity-attribute-value-eav-model-2ghg</guid>
      <description>&lt;p&gt;&lt;em&gt;(Example: Content Management System)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Have you ever faced a database design problem where the attributes of an entity vary widely across records?&lt;/p&gt;

&lt;p&gt;If yes, this article is for you.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Entity–Attribute–Value (EAV) model&lt;/strong&gt; is a database design pattern that allows us to define entity attributes &lt;strong&gt;at runtime&lt;/strong&gt;. In many real-world systems, a table can contain millions of records, where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Some entities have attributes &lt;strong&gt;X, Y, Z&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Others have completely different attributes&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Many attributes apply only to a small subset of records&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In such cases, using a traditional relational schema often leads to many &lt;strong&gt;NULL values&lt;/strong&gt; and frequent &lt;strong&gt;schema changes&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;With the EAV model, instead of adding new columns or storing NULLs, we &lt;strong&gt;define attributes separately&lt;/strong&gt; and assign values only when they apply to a specific entity instance.&lt;/p&gt;

&lt;p&gt;EAV models are commonly used in systems such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;E-commerce platforms&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Real-estate management systems&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Content management systems (CMS)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Product catalogs&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this article, we will explain how the &lt;strong&gt;Entity–Attribute–Value model&lt;/strong&gt; works by using a &lt;strong&gt;Content Management System (CMS)&lt;/strong&gt; as a practical example.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Cases
&lt;/h2&gt;

&lt;p&gt;The EAV model is powerful &lt;strong&gt;only when used in the right scenarios.&lt;/strong&gt; Below are the main reasons to consider using EAV in your database design.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to Use EAV
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;- Frequently changing attributes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When entities have many attributes that evolve over time (new attributes are added frequently).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;- Sparse data&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When most entities use only a small subset of available attributes, and the rest would otherwise be stored as NULL.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;- Custom attributes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When administrators or users need to define custom attributes at runtime without requiring database schema changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Not to Use EAV
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;- Stable and well-defined attributes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the attributes are fixed and rarely change, a traditional relational schema is a better choice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;- High-performance requirements&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;EAV models often require multiple joins, which can negatively impact query performance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;- Complex reporting and analytics&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Writing reports and analytical queries is more difficult and less efficient in EAV-based schemas.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important note:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Using EAV does &lt;strong&gt;not&lt;/strong&gt; mean every table in your system should follow this model.&lt;/p&gt;

&lt;p&gt;In practice, the best approach is often &lt;strong&gt;a hybrid model&lt;/strong&gt;, combining traditional tables with EAV tables based on the nature of each entity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Basic understanding of database systems&lt;/li&gt;
&lt;li&gt;Familiarity with relational databases and ER diagrams&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Project Setup
&lt;/h2&gt;

&lt;p&gt;To illustrate the EAV model, we will design an &lt;strong&gt;Entity Relationship Diagram (ERD)&lt;/strong&gt; for a simple content management system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the EAV ERD
&lt;/h2&gt;

&lt;p&gt;The classic EAV model consists of three main tables:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjpgvf0pmzdywdcoktnao.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjpgvf0pmzdywdcoktnao.png" alt=" " width="791" height="501"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Figure 1.0: Entity–Attribute–Value ER Diagram&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;- Entity&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Represents the main object whose attributes can vary over time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;- Attribute&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Stores the possible attributes that an entity can have.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;- AttributeValue&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Stores the actual value of a specific attribute for a specific entity.&lt;/p&gt;

&lt;p&gt;This structure allows attributes to be added dynamically without altering the database schema.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example: Content Management System (CMS)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;A Content Management System&lt;/strong&gt; is used to create, manage, and publish different types of digital content such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Blog posts&lt;/li&gt;
&lt;li&gt;Pages&lt;/li&gt;
&lt;li&gt;Events&lt;/li&gt;
&lt;li&gt;Courses&lt;/li&gt;
&lt;li&gt;Job postings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each content type has its own unique attributes. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An &lt;strong&gt;Event&lt;/strong&gt; has a location and start date&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;Job&lt;/strong&gt; has a salary and employment type&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;Page&lt;/strong&gt; may not require either&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To support this flexibility &lt;strong&gt;without constant schema changes, the EAV model&lt;/strong&gt; is an ideal solution.&lt;/p&gt;

&lt;p&gt;Below is a simplified ERD for a CMS using the EAV model&lt;br&gt;
 (Note: &lt;em&gt;This diagram may require refinement before production use&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa4z1h8yk0ffnw5ax798y.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa4z1h8yk0ffnw5ax798y.jpg" alt=" " width="800" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Figure 1.1: EAV Model for a Content Management System&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Tables Overview
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;- Content&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Represents the actual content entity with dynamic attributes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;- ContentType&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Defines the type of content (e.g., Blog, Event, Job).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;- Attribute&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Defines attributes specific to a content type.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;- ContentAttributeValue&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Stores the value of a specific attribute for a specific content item.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-World Table Instances
&lt;/h2&gt;

&lt;p&gt;Table &lt;strong&gt;ContentType&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2th4jgk99nt4i52k0gdg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2th4jgk99nt4i52k0gdg.png" alt=" " width="673" height="275"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Table &lt;strong&gt;Content&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8xbmmdoo3q7esypn4721.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8xbmmdoo3q7esypn4721.png" alt=" " width="662" height="210"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Table &lt;strong&gt;Attribute&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp9buv7rfkxq3kah0c98z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp9buv7rfkxq3kah0c98z.png" alt=" " width="690" height="227"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Table &lt;strong&gt;ContentAttributeValue (Core table)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fujh28lrddr1ry2wkjyqc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fujh28lrddr1ry2wkjyqc.png" alt=" " width="727" height="261"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From these examples, you can clearly see how the EAV model allows different content types to store only the attributes that apply to them—without wasting space or changing the schema.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;p&gt;The final step is implementing this model in a programming language or framework of your choice. This part is intentionally left to the reader, as covering it would significantly extend the length of this article.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Entity–Attribute–Value (EAV) model&lt;/strong&gt; is a flexible database design pattern that helps manage &lt;strong&gt;dynamic and evolving attributes&lt;/strong&gt; over time.&lt;/p&gt;

&lt;p&gt;In this article, we explored:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When to use and avoid EAV&lt;/li&gt;
&lt;li&gt;The core structure of the EAV model&lt;/li&gt;
&lt;li&gt;A real-world example using a Content Management System&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When applied correctly—often as part of a hybrid design—EAV can be a powerful solution for systems that require high flexibility.&lt;/p&gt;

&lt;h2&gt;
  
  
  Contact
&lt;/h2&gt;

&lt;p&gt;If you have any questions, feel free to reach out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;LinkedIn: &lt;a href="https://linkedin.com/in/dawit-girma-7b8867228/" rel="noopener noreferrer"&gt;https://linkedin.com/in/dawit-girma-7b8867228/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Email: &lt;a href="mailto:realdavis7779@gmail.com"&gt;realdavis7779@gmail.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Github: &lt;a href="https://github.com/dedawit" rel="noopener noreferrer"&gt;https://github.com/dedawit&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.dremio.com/wiki/entity-attribute-value-model/" rel="noopener noreferrer"&gt;https://www.dremio.com/wiki/entity-attribute-value-model/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://inviqa.com/blog/understanding-eav-data-model-and-when-use-it" rel="noopener noreferrer"&gt;https://inviqa.com/blog/understanding-eav-data-model-and-when-use-it&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>architecture</category>
      <category>backend</category>
      <category>database</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>A Practical Guide to Database Migrations in NestJS (TypeORM)</title>
      <dc:creator>Dawit Girma</dc:creator>
      <pubDate>Tue, 13 Jan 2026 11:15:01 +0000</pubDate>
      <link>https://forem.com/dedawit/a-practical-guide-to-database-migrations-in-nestjs-typeorm-jc7</link>
      <guid>https://forem.com/dedawit/a-practical-guide-to-database-migrations-in-nestjs-typeorm-jc7</guid>
      <description>&lt;p&gt;Database migrations are essential for managing database schemas (tables, columns, relationships) and data instances (records inside tables) in a safe and controlled manner.  Each migration consists of two methods: &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;- up&lt;/strong&gt; - applies actual change&lt;br&gt;
&lt;strong&gt;- down&lt;/strong&gt; - reverts the change made by the up method&lt;/p&gt;

&lt;p&gt;Many NestJs developers struggle with setting up migrations correctly. In this article, we will walk through creating, running and managing migrations in NestJs using TypeORM with PostgresSQL, using practical, step by step examples. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before starting, make sure you have the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Basic knowledge of &lt;strong&gt;NestJs&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;NodeJs 18+&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;NestJs 10+&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Basic knowledge of &lt;strong&gt;TypeORM&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Basic knowledge of &lt;strong&gt;PostgresSQL&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Project Setup
&lt;/h2&gt;

&lt;p&gt;We will create a minimal NestJs application to demonstrate migrations step by step.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Create a New NestJs Application&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Run the following command to create a new NestJs project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nest new nestjs-migration
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Configure TypeORM&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Create a &lt;strong&gt;config&lt;/strong&gt; folder in the project root and create a file named typeorm.config.ts.&lt;br&gt;
Install the required packages (TypeORM, PostgresSQL driver and configuration loader):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm i @nestjs/typeorm @nestjs/config typeorm pg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now paste the following code to &lt;strong&gt;config/typeorm.config.ts&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { TypeOrmModuleOptions } from '@nestjs/typeorm';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { DataSource, DataSourceOptions } from 'typeorm';

ConfigModule.forRoot({ isGlobal: true });

const configService = new ConfigService();
const DB_PORT = configService.getOrThrow&amp;lt;number&amp;gt;('DATABASE_PORT');

export const typeOrmConfig: TypeOrmModuleOptions = {
  type: 'postgres',
  host: configService.getOrThrow&amp;lt;string&amp;gt;('HOST_NAME'),
  port: DB_PORT,
  username: configService.getOrThrow&amp;lt;string&amp;gt;('DATABASE_USERNAME'),
  password: configService.getOrThrow&amp;lt;string&amp;gt;('DATABASE_PASSWORD'),
  database: configService.getOrThrow&amp;lt;string&amp;gt;('DATABASE_NAME'),
  entities: [__dirname + '/../**/*.entity.{js,ts}'],
  migrations: [__dirname + '/../src/migrations/*.{js,ts}'],
  synchronize: true,
};

export const dataSource = new DataSource(
  typeOrmConfig as DataSourceOptions,
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Environment Variables&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Create a &lt;strong&gt;.env&lt;/strong&gt; file in the project root and add the following values (replace them with your own credentials). &lt;br&gt;
&lt;strong&gt;Make sure to create a PostgresSQL database named nestjs_migration.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HOST_NAME=localhost
DATABASE_PORT=5432
DATABASE_USERNAME=your_username
DATABASE_PASSWORD=your_password
DATABASE_NAME=nestjs_migration
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Create entity&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Create a file named &lt;strong&gt;user.entity&lt;/strong&gt; inside the &lt;strong&gt;src&lt;/strong&gt; folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';

@Entity('user')
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column({ unique: true })
  email: string;

  @Column()
  password: string;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;5. Register TypeORM in AppModule&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Update &lt;strong&gt;app.module.ts&lt;/strong&gt; as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { typeOrmConfig } from 'config/typeorm.config';
import { User } from './user.entity';

@Module({
  imports: [
    TypeOrmModule.forRoot(typeOrmConfig),
    TypeOrmModule.forFeature([User]),
  ],
})
export class AppModule {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;6. Add TypeORM Migration Scripts&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Add the following scripts to the &lt;strong&gt;scripts&lt;/strong&gt; section of &lt;strong&gt;package.json&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Linux / macOS&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"typeorm": "ts-node ./node_modules/typeorm/cli",
"migration:run": "npm run typeorm migration:run -- -d ./config/typeorm.config.ts",
"migration:generate": "npm run typeorm -- -d ./config/typeorm.config.ts migration:generate ./src/migrations/$npm_config_name",
"migration:create": "npm run typeorm -- migration:create ./src/migrations/$npm_config_name",
"migration:revert": "npm run typeorm -- -d ./config/typeorm.config.ts migration:revert"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Windows&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"typeorm": "ts-node ./node_modules/typeorm/cli",
"migration:run": "npm run typeorm migration:run -- -d ./config/typeorm.config.ts",
"migration:generate": "npm run typeorm -- -d ./config/typeorm.config.ts migration:generate ./src/migrations/%npm_config_name%",
"migration:create": "npm run typeorm -- migration:create ./src/migrations/%npm_config_name%",
"migration:revert": "npm run typeorm -- -d ./config/typeorm.config.ts migration:revert"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;7. Initial Database Sync&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Run the application to create the initial &lt;strong&gt;user&lt;/strong&gt; table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run start:dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ensure &lt;strong&gt;synchronize:true&lt;/strong&gt; is enabled in the database configuration at this stage.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Understanding Migration Commands&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. typeorm&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"typeorm": "ts-node ./node_modules/typeorm/cli"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command allows us to run the TypeORM CLI using &lt;strong&gt;ts-node&lt;/strong&gt;, enabling the execution of Typescript files without building the project. All migrations rely on this command.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. migration:generate&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before generating migrations, set &lt;strong&gt;synchronize&lt;/strong&gt; to &lt;strong&gt;false&lt;/strong&gt; to prevent TypeORM from automatically applying schema changes.&lt;br&gt;
Let us add a new &lt;strong&gt;username&lt;/strong&gt; field to &lt;strong&gt;User&lt;/strong&gt; entity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Column()
username: string;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now generate a migration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
npm run migration:generate --name=username-field-addition
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The command generates migration similar to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { MigrationInterface, QueryRunner } from 'typeorm';

export class UsernameFieldAddition1768070224042
  implements MigrationInterface
{
  public async up(queryRunner: QueryRunner): Promise&amp;lt;void&amp;gt; {
    await queryRunner.query(
      `ALTER TABLE "user" ADD "username" character varying NOT NULL`,
    );
  }

  public async down(queryRunner: QueryRunner): Promise&amp;lt;void&amp;gt; {
    await queryRunner.query(
      `ALTER TABLE "user" DROP COLUMN "username"`,
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. migration:run&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run migration:run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command executes all pending migrations by running their up methods. TypeORM tracks executed migrations in a dedicated table to prevent re-runnig them.&lt;/p&gt;

&lt;p&gt;In order to apply the changes in the generated migration above, we need to execute it. Hence, run the &lt;strong&gt;migration:run&lt;/strong&gt; command to add &lt;strong&gt;username&lt;/strong&gt; in &lt;strong&gt;user&lt;/strong&gt; table in the database.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. migration:revert&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run migration:revert
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This runs the down method of the migration, reverting the changes. After reverting, delete the migration file to avoid reapplying it later. &lt;/p&gt;

&lt;p&gt;If we run the &lt;strong&gt;migration:revert&lt;/strong&gt; command above, the username field will be removed.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. migration:create&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The command creates empty migration for manual changes, commonly used for inserting initial data (e.g. a superadmin user account)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
npm run migration:create --name=superadmin-user
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above migration creates a migration with the name &lt;strong&gt;superadmin-user&lt;/strong&gt;. You can use any name you want. &lt;br&gt;
Generated migration&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { MigrationInterface, QueryRunner } from 'typeorm';

export class SuperadminUser1768073019893
  implements MigrationInterface
{
  public async up(queryRunner: QueryRunner): Promise&amp;lt;void&amp;gt; {}

  public async down(queryRunner: QueryRunner): Promise&amp;lt;void&amp;gt; {}
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Modify the above migration manually to insert superadmin user:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { MigrationInterface, QueryRunner } from 'typeorm';

export class SuperadminUser1768073019893
  implements MigrationInterface
{
  public async up(queryRunner: QueryRunner): Promise&amp;lt;void&amp;gt; {
    await queryRunner.query(
      `
      INSERT INTO "user" (email, password)
      VALUES ($1, $2)
      ON CONFLICT (email) DO NOTHING
      `,
      ['superadmin@example.com', 'superadmin123'],
    );
  }

  public async down(queryRunner: QueryRunner): Promise&amp;lt;void&amp;gt; {
    await queryRunner.query(
      `
      DELETE FROM "user"
      WHERE email = $1
      `,
      ['superadmin@example.com'],
    );
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the migration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run migration:run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The superadmin user record will now exist in the database.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Database migrations are powerful tools for managing both the &lt;strong&gt;schema changes&lt;/strong&gt; and &lt;strong&gt;data updates&lt;/strong&gt; safely. This article demonstrated a practical and straightforward approach to handling migrations in &lt;strong&gt;NestJS with TypeORM,&lt;/strong&gt; from setup to advanced use cases.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Contact&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you have any questions, feel free to reach out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;LinkedIn: &lt;a href="https://linkedin.com/in/dawit-girma-7b8867228/" rel="noopener noreferrer"&gt;https://linkedin.com/in/dawit-girma-7b8867228/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Email: &lt;a href="mailto:realdavis7779@gmail.com"&gt;realdavis7779@gmail.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Github: &lt;a href="https://github.com/dedawit" rel="noopener noreferrer"&gt;https://github.com/dedawit&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The final code is available at public repository:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/dedawit/nestjs-migration.git" rel="noopener noreferrer"&gt;https://github.com/dedawit/nestjs-migration.git&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;References&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.nestjs.com/" rel="noopener noreferrer"&gt;https://docs.nestjs.com/&lt;/a&gt;&lt;br&gt;
&lt;a href="https://typeorm.io/docs/migrations/why" rel="noopener noreferrer"&gt;https://typeorm.io/docs/migrations/why&lt;/a&gt;&lt;/p&gt;

</description>
      <category>database</category>
      <category>postgres</category>
      <category>nestjs</category>
      <category>backenddevelopment</category>
    </item>
  </channel>
</rss>
