<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Shailesh's Blog]]></title><description><![CDATA[Shailesh's Blog]]></description><link>https://blog.shaileshhb.in</link><generator>RSS for Node</generator><lastBuildDate>Wed, 13 May 2026 19:02:01 GMT</lastBuildDate><atom:link href="https://blog.shaileshhb.in/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Detecting Duplicates in Angular FormArray]]></title><description><![CDATA[If you’ve worked with Angular Reactive Forms, you’ve probably built a form that dynamically grows — you click Add, and a new input appears. It’s clean, declarative, and Reactive in the truest sense.
But one fine day, you face a subtle problem:

“How ...]]></description><link>https://blog.shaileshhb.in/detecting-duplicates-in-angular-formarray</link><guid isPermaLink="true">https://blog.shaileshhb.in/detecting-duplicates-in-angular-formarray</guid><category><![CDATA[Angular]]></category><category><![CDATA[reactive forms angular]]></category><category><![CDATA[Angular | Form Array |Dynamic Form ]]></category><category><![CDATA[reactive forms]]></category><category><![CDATA[#angularforms]]></category><category><![CDATA[Validation]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[Frontend Development]]></category><dc:creator><![CDATA[Shailesh B]]></dc:creator><pubDate>Fri, 10 Oct 2025 11:01:12 GMT</pubDate><content:encoded><![CDATA[<p>If you’ve worked with <strong>Angular Reactive Forms</strong>, you’ve probably built a form that dynamically grows — you click <strong>Add</strong>, and a new input appears. It’s clean, declarative, and <em>Reactive</em> in the truest sense.</p>
<p>But one fine day, you face a subtle problem:</p>
<blockquote>
<p>“How do I make sure no two inputs have the same value?”</p>
</blockquote>
<p>That’s where this story begins.</p>
<h2 id="heading-the-setup">The Setup</h2>
<p>Let’s imagine a form where users can add their <strong>skills</strong> dynamically.</p>
<p>Each skill sits inside a <code>FormGroup</code>, and all such groups live inside a <code>FormArray</code> called <code>userHobby</code>.</p>
<pre><code class="lang-typescript">userForm = <span class="hljs-keyword">new</span> FormGroup({
  userHobby: <span class="hljs-keyword">new</span> FormArray([], [Validators.required]),
});
</code></pre>
<p>When the user clicks <em>Add New Row</em>, we push a new <code>FormGroup</code> into the array:</p>
<pre><code class="lang-typescript">addNewRow(): <span class="hljs-built_in">void</span> {
  <span class="hljs-built_in">this</span>.userHobbyControl.push(
    <span class="hljs-keyword">new</span> FormGroup({
      skill: <span class="hljs-keyword">new</span> FormControl&lt;<span class="hljs-built_in">string</span> | <span class="hljs-literal">null</span>&gt;(<span class="hljs-literal">null</span>, [
        Validators.required,
        <span class="hljs-built_in">this</span>.uniqueSkillValidator(),
      ]),
    })
  );
}
</code></pre>
<p>Nothing fancy yet — a normal reactive form with required validation.<br />The fun starts now.</p>
<hr />
<p>The Problem — Detecting Duplicates<br />Let’s say the user adds:</p>
<ul>
<li><p>“Angular”</p>
</li>
<li><p>“React”</p>
</li>
<li><p>“Angular”</p>
</li>
</ul>
<p>We want to catch that third input — it shouldn’t be allowed.<br />You’d think this is simple: just look for duplicates in the array and mark errors. But the challenge here is <strong>context</strong>.<br />Each validator in Angular runs <em>in isolation</em> — it only knows about its control, not its siblings.<br />So how do we make one control’s validator aware of the others?</p>
<hr />
<h2 id="heading-the-trick-closing-over-the-parent">The Trick — Closing Over the Parent</h2>
<p>The key is closure — using a function that captures the parent context (<code>this.userHobbyControl</code>) while validating each control.</p>
<pre><code class="lang-typescript">uniqueSkillValidator(): ValidatorFn {
  <span class="hljs-keyword">return</span> (control: AbstractControl): ValidationErrors | <span class="hljs-function"><span class="hljs-params">null</span> =&gt;</span> {
    <span class="hljs-keyword">let</span> skills: <span class="hljs-built_in">string</span>[] = [];

    <span class="hljs-keyword">for</span> (
      <span class="hljs-keyword">let</span> index = <span class="hljs-number">0</span>;
      index &lt;= <span class="hljs-built_in">this</span>.userHobbyControl.controls.length;
      index++
    ) {
      <span class="hljs-keyword">const</span> group = <span class="hljs-built_in">this</span>.userHobbyControl.controls[index];
      <span class="hljs-keyword">const</span> skill = group?.get(<span class="hljs-string">'skill'</span>)?.value;
      <span class="hljs-keyword">if</span> (skill) skills.push(skill);
    }

    <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.hasDuplicates(skills)) {
      <span class="hljs-keyword">return</span> { duplicate: <span class="hljs-literal">true</span> };
    }

    <span class="hljs-comment">// Reset errors if no duplicates</span>
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> group <span class="hljs-keyword">of</span> <span class="hljs-built_in">this</span>.userHobbyControl.controls) {
      group.get(<span class="hljs-string">'skill'</span>)?.setErrors(<span class="hljs-literal">null</span>);
    }

    <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
  };
}
</code></pre>
<p>Here, <code>uniqueSkillValidator()</code> creates a validator function that <strong>closes over</strong> the <code>userHobbyControl</code> FormArray.<br />This gives it visibility into all sibling controls — and that’s how we make validation context-aware.</p>
<hr />
<h2 id="heading-detecting-duplicates-the-set-trick">Detecting Duplicates — The Set Trick</h2>
<p>The check itself is beautifully simple:</p>
<pre><code class="lang-typescript">hasDuplicates(skills: <span class="hljs-built_in">string</span>[]) {
  <span class="hljs-keyword">const</span> noDups = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Set</span>(skills);
  <span class="hljs-keyword">return</span> skills.length !== noDups.size;
}
</code></pre>
<p>This is a common pattern in JavaScript — using a <code>Set</code> to strip duplicates and comparing lengths.</p>
<hr />
<h2 id="heading-the-validator-lifecycle">The Validator Lifecycle</h2>
<p>Every time the user types in any <code>skill</code>, Angular re-runs its validator.</p>
<p>Here’s what happens under the hood:</p>
<ol>
<li><p>Collect all the <code>skill</code> values.</p>
</li>
<li><p>Check if any duplicates exist.</p>
</li>
<li><p>If yes, mark all the duplicates with <code>{ duplicate: true }</code>.</p>
</li>
<li><p>If not, clear all duplicate errors.</p>
</li>
</ol>
<p>The form instantly reacts — that’s the reactive in Reactive Forms.</p>
<hr />
<h2 id="heading-why-this-works-beautifully">Why This Works Beautifully</h2>
<p>This approach looks deceptively simple, but it captures three deep ideas:</p>
<ul>
<li><p><strong>Validation by closure</strong> — validators can be dynamic and context-aware.</p>
</li>
<li><p><strong>State synchronization</strong> — every keystroke re-validates all controls.</p>
</li>
<li><p><strong>Minimal logic, maximum clarity</strong> — the heart of good Angular design.</p>
</li>
</ul>
<hr />
<h3 id="heading-tldr">TL;DR</h3>
<p>✅ Use <code>FormArray</code> for dynamic forms.<br />✅ Write a custom validator using closure to access sibling controls.<br />✅ Use <code>Set</code> to detect duplicates.<br />✅ Keep your form reactive — let Angular do the rest.</p>
<hr />
<h3 id="heading-try-it-yourself">🧩 Try it yourself</h3>
<p>You can explore the full working example on <a target="_blank" href="https://stackblitz.com/edit/unique-value-validation">StackBlitz</a> here:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://stackblitz.com/edit/unique-value-validation">https://stackblitz.com/edit/unique-value-validation</a></div>
]]></content:encoded></item><item><title><![CDATA[Step-by-Step Guide to Building a Task Scheduler in Go]]></title><description><![CDATA[Many applications need background jobs like syncing data, cleaning records, or sending emails. To handle this we can build a task scheduler that runs jobs at the right time without blocking the main application. In this blog we will see how to write ...]]></description><link>https://blog.shaileshhb.in/step-by-step-guide-to-building-a-task-scheduler-in-go</link><guid isPermaLink="true">https://blog.shaileshhb.in/step-by-step-guide-to-building-a-task-scheduler-in-go</guid><category><![CDATA[golang]]></category><category><![CDATA[Programming Blogs]]></category><category><![CDATA[developers]]></category><category><![CDATA[backend]]></category><category><![CDATA[concurrency-in-go]]></category><category><![CDATA[task scheduler]]></category><category><![CDATA[scheduler]]></category><dc:creator><![CDATA[Shailesh B]]></dc:creator><pubDate>Sat, 20 Sep 2025 14:05:04 GMT</pubDate><content:encoded><![CDATA[<p>Many applications need background jobs like syncing data, cleaning records, or sending emails. To handle this we can build a task scheduler that runs jobs at the right time without blocking the main application. In this blog we will see how to write production-ready scheduler code in Go, and also cover important parts like concurrency, recurrence, and error handling.</p>
<h3 id="heading-high-level-design">High-Level Design</h3>
<p>Before we jump into code, let us understand what we are trying to build. A scheduler has three main parts:</p>
<ol>
<li><p><strong>Scheduler Service</strong> – This is the main manager. It keeps a list of tasks, starts them, stops them, and can reload them if needed.</p>
</li>
<li><p><strong>Task</strong> – A task is one job that we want to run again and again. For example, calling an API every 5 minutes.</p>
</li>
<li><p><strong>Runnable Function</strong> – This is the actual code that the task will execute. We keep this separate so that the scheduler is generic and can run any kind of job.</p>
</li>
</ol>
<p>The design is simple: the <strong>Scheduler Service</strong> holds multiple <strong>Tasks</strong>, and each task has a <strong>Runnable Function</strong> attached to it. The task uses a <strong>ticker</strong> and a <strong>goroutine</strong> to run at the correct interval, while the service makes sure tasks can be managed safely with locks.</p>
<h3 id="heading-defining-the-task-model">Defining the Task Model</h3>
<p>Each scheduled job is represented by a <strong>Task</strong>. A task should know:</p>
<ul>
<li><p>its <strong>ID</strong> (unique name or identifier),</p>
</li>
<li><p>its <strong>interval</strong> (how often to run),</p>
</li>
<li><p>its <strong>status</strong> (new, started, or running),</p>
</li>
<li><p>and some internal fields to manage execution (like ticker, cancel function, etc.).</p>
</li>
</ul>
<p>A simple version of the <code>Task</code> struct can look like this:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> taskStatus <span class="hljs-keyword">int</span>

<span class="hljs-keyword">const</span> (
    statusNew taskStatus = <span class="hljs-literal">iota</span>
    statusStarted
    statusRunning
)

<span class="hljs-keyword">type</span> Task <span class="hljs-keyword">struct</span> {
    ID        <span class="hljs-keyword">string</span>
    Interval  time.Duration
    Status    taskStatus
    cancel    context.CancelFunc
    ticker    *time.Ticker
    lastRun   *time.Time
    fn        <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(ctx context.Context)</span> <span class="hljs-title">error</span></span>
}
</code></pre>
<p>Here,</p>
<ul>
<li><p><code>fn</code> is the function that will run when the task executes.</p>
</li>
<li><p><code>ticker</code> is used to schedule execution at fixed intervals.</p>
</li>
<li><p><code>cancel</code> is used to stop the task gracefully.</p>
</li>
<li><p><code>lastRun</code> helps in calculating the next run time.</p>
</li>
</ul>
<p>This structure keeps the task generic, so it can run <strong>any kind of function</strong> we provide.</p>
<h3 id="heading-scheduler-service">Scheduler Service</h3>
<p>The <strong>Scheduler Service</strong> is the main manager. It keeps track of all tasks, starts them, stops them, and reloads them if needed. It also ensures that multiple tasks can run safely at the same time without conflicts.</p>
<p>A simple <code>Scheduler</code> struct can look like this:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> Scheduler <span class="hljs-keyword">struct</span> {
    tasks <span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]*Task
    mtx   sync.Mutex
}
</code></pre>
<p>Here, <code>tasks</code> is a map of active tasks, and <code>mtx</code> is a mutex to protect access to this map.</p>
<p>Some core methods of the service are:</p>
<ol>
<li><p><strong>AddTask</strong> – Adds a new task to the scheduler and starts it.</p>
</li>
<li><p><strong>StartTask</strong> – Runs a single task manually.</p>
</li>
<li><p><strong>Reload</strong> – Stops tasks that are no longer needed and starts new ones.</p>
</li>
<li><p><strong>ListTasks</strong> – Returns a list of all current tasks and their status.</p>
</li>
</ol>
<p>For example, adding a task could look like this:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(s *Scheduler)</span> <span class="hljs-title">AddTask</span><span class="hljs-params">(t *Task)</span></span> {
    s.mtx.Lock()
    <span class="hljs-keyword">defer</span> s.mtx.Unlock()
    s.tasks[t.ID] = t
    t.start() <span class="hljs-comment">// start the task immediately</span>
}
</code></pre>
<p>The scheduler makes sure that all operations on tasks are <strong>thread-safe</strong>, so adding, removing, or running tasks does not cause race conditions.</p>
<h3 id="heading-starting-and-stopping-tasks">Starting and Stopping Tasks</h3>
<p>Each task runs in its own <strong>goroutine</strong> and uses a <strong>ticker</strong> to trigger the job at the right interval. We also use a <strong>cancellable context</strong> to stop the task gracefully when needed.</p>
<p>A simple way to start a task could be:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(t *Task)</span> <span class="hljs-title">start</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">if</span> t.Status &gt;= statusStarted {
        <span class="hljs-keyword">return</span>
    }

    ctx, cancel := context.WithCancel(context.Background())
    t.cancel = cancel
    t.ticker = time.NewTicker(t.Interval)
    t.Status = statusStarted

    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">for</span> {
            <span class="hljs-keyword">select</span> {
            <span class="hljs-keyword">case</span> &lt;-ctx.Done():
                t.ticker.Stop()
                t.Status = statusNew
                <span class="hljs-keyword">return</span>
            <span class="hljs-keyword">case</span> &lt;-t.ticker.C:
                t.Status = statusRunning
                t.fn(ctx) <span class="hljs-comment">// run the task function</span>
                t.Status = statusStarted
            }
        }
    }()
}
</code></pre>
<p>To stop a task safely:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(t *Task)</span> <span class="hljs-title">stop</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">if</span> t.Status == statusRunning &amp;&amp; t.cancel != <span class="hljs-literal">nil</span> {
        t.cancel()
    }
    <span class="hljs-keyword">if</span> t.ticker != <span class="hljs-literal">nil</span> {
        t.ticker.Stop()
    }
}
</code></pre>
<p>We can also trigger a task <strong>manually</strong>, without waiting for the next ticker tick:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(t *Task)</span> <span class="hljs-title">runNow</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">go</span> t.fn(context.Background())
}
</code></pre>
<p><strong>Key points:</strong></p>
<ul>
<li><p>Each task has its own goroutine and ticker.</p>
</li>
<li><p>The cancellable context allows the task to stop gracefully.</p>
</li>
<li><p>Using mutexes (or status flags) ensures tasks do not run multiple times concurrently.</p>
</li>
</ul>
<h3 id="heading-scheduling-logic">Scheduling Logic</h3>
<p>A task is usually scheduled to run repeatedly at a fixed interval. For example, every 5 minutes, every hour, or every day. The scheduler needs to calculate <strong>when the next run should happen</strong>, based on the last run or a defined start time.</p>
<p>Here’s a simplified example:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(t *Task)</span> <span class="hljs-title">nextRun</span><span class="hljs-params">()</span> <span class="hljs-title">time</span>.<span class="hljs-title">Duration</span></span> {
    <span class="hljs-keyword">if</span> t.lastRun == <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> t.Interval <span class="hljs-comment">// first run uses the defined interval</span>
    }

    next := t.lastRun.Add(t.Interval)
    now := time.Now()
    <span class="hljs-keyword">if</span> next.Before(now) {
        <span class="hljs-comment">// if we missed the scheduled time (system was down), fast-forward</span>
        missedIntervals := (now.Sub(next) / t.Interval) + <span class="hljs-number">1</span>
        next = next.Add(missedIntervals * t.Interval)
    }

    <span class="hljs-keyword">return</span> next.Sub(now)
}
</code></pre>
<p><strong>Explanation:</strong></p>
<ul>
<li><p><code>t.lastRun</code> tracks when the task ran successfully last time.</p>
</li>
<li><p>If <code>lastRun</code> is <code>nil</code>, it’s the first run, so we use the interval directly.</p>
</li>
<li><p>If the next scheduled time is in the past (maybe the system was down), we <strong>fast-forward</strong> to the next valid time.</p>
</li>
</ul>
<p>The scheduler uses this duration to <strong>reset the ticker</strong>, ensuring that tasks run at the correct intervals even after delays or downtime.</p>
<p><strong>Key points:</strong></p>
<ul>
<li><p>Recurrence calculation is critical to ensure tasks are reliable.</p>
</li>
<li><p>The scheduler can handle missed runs automatically.</p>
</li>
<li><p>By tracking <code>lastRun</code>, we can make the system resilient and predictable.</p>
</li>
</ul>
<h3 id="heading-concurrency-and-safety">Concurrency and Safety</h3>
<p>Tasks run in separate goroutines, and the scheduler manages multiple tasks at once. To avoid race conditions, we use <strong>mutexes</strong> (<code>sync.Mutex</code> or <code>sync.RWMutex</code>).</p>
<ul>
<li>The <strong>scheduler</strong> locks the task map when adding, removing, or listing tasks:</li>
</ul>
<pre><code class="lang-go">s.mtx.Lock()
<span class="hljs-keyword">defer</span> s.mtx.Unlock()
</code></pre>
<ul>
<li>Each <strong>task</strong> uses its own mutex when updating status or last run time:</li>
</ul>
<pre><code class="lang-go">t.mtx.Lock()
t.Status = statusRunning
t.mtx.Unlock()
</code></pre>
<p>This ensures tasks do not run twice at the same time and that adding or removing tasks is safe, keeping the scheduler reliable.</p>
<h3 id="heading-error-handling-and-recovery">Error Handling and Recovery</h3>
<p>Tasks can fail or even panic while running. A good scheduler should <strong>catch errors</strong>, <strong>log them</strong>, and keep running other tasks without crashing.</p>
<ul>
<li>Wrap task execution in a <strong>recover block</strong> to handle panics:</li>
</ul>
<pre><code class="lang-go"><span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">defer</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">if</span> r := <span class="hljs-built_in">recover</span>(); r != <span class="hljs-literal">nil</span> {
            log.Println(<span class="hljs-string">"Task panicked:"</span>, r)
        }
    }()
    err := t.fn(ctx)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        log.Println(<span class="hljs-string">"Task error:"</span>, err)
    }
}()
</code></pre>
<ul>
<li><p>Log the <strong>start time, end time, and duration</strong> for monitoring and debugging.</p>
</li>
<li><p>Keep track of <strong>last successful run</strong> to calculate the next run correctly.</p>
</li>
</ul>
<p>By handling errors and panics, the scheduler becomes <strong>resilient</strong> and tasks continue running even if one fails.</p>
<h3 id="heading-extending-with-business-logic">Extending with Business Logic</h3>
<p>The scheduler itself is generic — it does not care what the task actually does. To run real jobs, we attach a <strong>Runnable function</strong> to each task.</p>
<p>A <strong>Runnable</strong> is just a Go function with a simple signature:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> Runnable <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(ctx context.Context)</span> <span class="hljs-title">error</span></span>
</code></pre>
<p>When adding a task, we pass this function:</p>
<pre><code class="lang-go">task := &amp;Task{
    ID:       <span class="hljs-string">"heartbeat"</span>,
    Interval: <span class="hljs-number">10</span> * time.Second,
    fn: <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(ctx context.Context)</span> <span class="hljs-title">error</span></span> {
        fmt.Println(<span class="hljs-string">"Heartbeat at"</span>, time.Now())
        <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
    },
}
scheduler.AddTask(task)
</code></pre>
<p>Now the scheduler can run this function repeatedly at the defined interval.</p>
<p><strong>Key points:</strong></p>
<ul>
<li><p>Decoupling task logic from the scheduler makes it reusable.</p>
</li>
<li><p>Any job (API call, cleanup, email, etc.) can be scheduled the same way.</p>
</li>
<li><p>Manual triggers and automatic scheduling work for all task types.</p>
</li>
</ul>
<h3 id="heading-practical-usage-examples">Practical Usage Examples</h3>
<p>Once we have the scheduler and task structs ready, using them is simple.</p>
<h4 id="heading-1-create-the-scheduler">1. Create the Scheduler</h4>
<pre><code class="lang-go">scheduler := &amp;Scheduler{
    tasks: <span class="hljs-built_in">make</span>(<span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]*Task),
}
</code></pre>
<h4 id="heading-2-add-a-task">2. Add a Task</h4>
<pre><code class="lang-go">task1 := &amp;Task{
    ID:       <span class="hljs-string">"heartbeat"</span>,
    Interval: <span class="hljs-number">5</span> * time.Second,
    fn: <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(ctx context.Context)</span> <span class="hljs-title">error</span></span> {
        fmt.Println(<span class="hljs-string">"Heartbeat at"</span>, time.Now())
        <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
    },
}
scheduler.AddTask(task1)
</code></pre>
<h4 id="heading-3-add-another-task">3. Add Another Task</h4>
<pre><code class="lang-go">task2 := &amp;Task{
    ID:       <span class="hljs-string">"cleanup"</span>,
    Interval: <span class="hljs-number">10</span> * time.Second,
    fn: <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(ctx context.Context)</span> <span class="hljs-title">error</span></span> {
        fmt.Println(<span class="hljs-string">"Cleaning up old data at"</span>, time.Now())
        <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
    },
}
scheduler.AddTask(task2)
</code></pre>
<h4 id="heading-4-manual-trigger">4. Manual Trigger</h4>
<p>You can run a task immediately without waiting for the next scheduled interval:</p>
<pre><code class="lang-go">task1.runNow()
</code></pre>
<h4 id="heading-5-list-all-tasks">5. List All Tasks</h4>
<pre><code class="lang-go"><span class="hljs-keyword">for</span> _, t := <span class="hljs-keyword">range</span> scheduler.ListTasks() {
    fmt.Println(t.ID, <span class="hljs-string">"Status:"</span>, t.Status)
}
</code></pre>
<p><strong>Key Points:</strong></p>
<ul>
<li><p>Adding tasks is simple and flexible.</p>
</li>
<li><p>Any function can be scheduled — API calls, cleanup jobs, logs, or emails.</p>
</li>
<li><p>Manual triggers allow testing or one-off execution.</p>
</li>
<li><p>The scheduler takes care of concurrency, next run calculation, and error handling.</p>
</li>
</ul>
<h3 id="heading-full-working-scheduler-code">Full Working Scheduler Code</h3>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"context"</span>
    <span class="hljs-string">"fmt"</span>
    <span class="hljs-string">"sync"</span>
    <span class="hljs-string">"time"</span>
)

<span class="hljs-comment">// ----------------------- Task -----------------------</span>

<span class="hljs-keyword">type</span> taskStatus <span class="hljs-keyword">int</span>

<span class="hljs-keyword">const</span> (
    statusNew taskStatus = <span class="hljs-literal">iota</span>
    statusStarted
    statusRunning
)

<span class="hljs-keyword">type</span> Task <span class="hljs-keyword">struct</span> {
    ID        <span class="hljs-keyword">string</span>
    Interval  time.Duration
    Status    taskStatus
    cancel    context.CancelFunc
    ticker    *time.Ticker
    startTime time.Time
    lastRun   *time.Time
    fn        <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(ctx context.Context)</span> <span class="hljs-title">error</span></span>
    mtx       sync.Mutex
}

<span class="hljs-comment">// Start the task</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(t *Task)</span> <span class="hljs-title">start</span><span class="hljs-params">()</span></span> {
    t.mtx.Lock()
    <span class="hljs-keyword">if</span> t.Status &gt;= statusStarted {
        t.mtx.Unlock()
        <span class="hljs-keyword">return</span>
    }

    ctx, cancel := context.WithCancel(context.Background())
    t.cancel = cancel
    t.ticker = time.NewTicker(t.Interval)
    t.Status = statusStarted
    t.mtx.Unlock()

    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">for</span> {
            <span class="hljs-keyword">select</span> {
            <span class="hljs-keyword">case</span> &lt;-ctx.Done():
                t.ticker.Stop()
                t.mtx.Lock()
                t.Status = statusNew
                t.mtx.Unlock()
                <span class="hljs-keyword">return</span>
            <span class="hljs-keyword">case</span> &lt;-t.ticker.C:
                t.mtx.Lock()
                t.Status = statusRunning
                t.mtx.Unlock()

                <span class="hljs-keyword">defer</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
                    <span class="hljs-keyword">if</span> r := <span class="hljs-built_in">recover</span>(); r != <span class="hljs-literal">nil</span> {
                        fmt.Println(<span class="hljs-string">"Task panicked:"</span>, r)
                    }
                }()

                err := t.fn(ctx)
                <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
                    fmt.Println(<span class="hljs-string">"Task error:"</span>, err)
                }

                now := time.Now()
                t.mtx.Lock()
                t.lastRun = &amp;now
                t.Status = statusStarted
                t.mtx.Unlock()
            }
        }
    }()
}

<span class="hljs-comment">// Stop the task</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(t *Task)</span> <span class="hljs-title">stop</span><span class="hljs-params">()</span></span> {
    t.mtx.Lock()
    <span class="hljs-keyword">defer</span> t.mtx.Unlock()
    <span class="hljs-keyword">if</span> t.cancel != <span class="hljs-literal">nil</span> {
        t.cancel()
    }
    <span class="hljs-keyword">if</span> t.ticker != <span class="hljs-literal">nil</span> {
        t.ticker.Stop()
    }
    t.Status = statusNew
}

<span class="hljs-comment">// Run the task immediately</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(t *Task)</span> <span class="hljs-title">runNow</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">go</span> t.fn(context.Background())
}

<span class="hljs-comment">// ----------------------- Scheduler -----------------------</span>

<span class="hljs-keyword">type</span> Scheduler <span class="hljs-keyword">struct</span> {
    tasks <span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]*Task
    mtx   sync.Mutex
}

<span class="hljs-comment">// AddTask adds and starts a task</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(s *Scheduler)</span> <span class="hljs-title">AddTask</span><span class="hljs-params">(t *Task)</span></span> {
    s.mtx.Lock()
    <span class="hljs-keyword">defer</span> s.mtx.Unlock()
    s.tasks[t.ID] = t
    t.start()
}

<span class="hljs-comment">// StopTask stops a task by ID</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(s *Scheduler)</span> <span class="hljs-title">StopTask</span><span class="hljs-params">(id <span class="hljs-keyword">string</span>)</span></span> {
    s.mtx.Lock()
    <span class="hljs-keyword">defer</span> s.mtx.Unlock()
    <span class="hljs-keyword">if</span> t, ok := s.tasks[id]; ok {
        t.stop()
        <span class="hljs-built_in">delete</span>(s.tasks, id)
    }
}

<span class="hljs-comment">// ListTasks returns all tasks</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(s *Scheduler)</span> <span class="hljs-title">ListTasks</span><span class="hljs-params">()</span> []*<span class="hljs-title">Task</span></span> {
    s.mtx.Lock()
    <span class="hljs-keyword">defer</span> s.mtx.Unlock()
    tasks := []*Task{}
    <span class="hljs-keyword">for</span> _, t := <span class="hljs-keyword">range</span> s.tasks {
        tasks = <span class="hljs-built_in">append</span>(tasks, t)
    }
    <span class="hljs-keyword">return</span> tasks
}

<span class="hljs-comment">// ----------------------- Example Usage -----------------------</span>

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    scheduler := &amp;Scheduler{
        tasks: <span class="hljs-built_in">make</span>(<span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]*Task),
    }

    <span class="hljs-comment">// Heartbeat task</span>
    task1 := &amp;Task{
        ID:       <span class="hljs-string">"heartbeat"</span>,
        Interval: <span class="hljs-number">5</span> * time.Second,
        fn: <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(ctx context.Context)</span> <span class="hljs-title">error</span></span> {
            fmt.Println(<span class="hljs-string">"Heartbeat at"</span>, time.Now())
            <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
        },
    }
    scheduler.AddTask(task1)

    <span class="hljs-comment">// Cleanup task</span>
    task2 := &amp;Task{
        ID:       <span class="hljs-string">"cleanup"</span>,
        Interval: <span class="hljs-number">10</span> * time.Second,
        fn: <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(ctx context.Context)</span> <span class="hljs-title">error</span></span> {
            fmt.Println(<span class="hljs-string">"Cleaning up old data at"</span>, time.Now())
            <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
        },
    }
    scheduler.AddTask(task2)

    <span class="hljs-comment">// Manual run example</span>
    task1.runNow()

    <span class="hljs-comment">// List all tasks</span>
    <span class="hljs-keyword">for</span> _, t := <span class="hljs-keyword">range</span> scheduler.ListTasks() {
        fmt.Println(<span class="hljs-string">"Task:"</span>, t.ID, <span class="hljs-string">"Status:"</span>, t.Status)
    }

    <span class="hljs-comment">// Keep the program running</span>
    <span class="hljs-keyword">select</span> {}
}
</code></pre>
<h3 id="heading-conclusion-and-takeaways">Conclusion and Takeaways</h3>
<p><strong>Key lessons from this implementation:</strong></p>
<ul>
<li><p>Keep the <strong>scheduler generic</strong> so it can run any job.</p>
</li>
<li><p>Use <strong>goroutines and tickers</strong> to run tasks at intervals.</p>
</li>
<li><p>Protect shared data with <strong>mutexes</strong> to avoid race conditions.</p>
</li>
<li><p>Handle <strong>errors and panics</strong> to keep tasks running reliably.</p>
</li>
<li><p>Track <strong>last run times</strong> to calculate next runs and recover from downtime.</p>
</li>
<li><p>Decouple <strong>task logic from scheduler</strong> for flexibility and easier testing.</p>
</li>
</ul>
<p><strong>Note:</strong> This blog was created with assistance from <strong>ChatGPT</strong></p>
]]></content:encoded></item><item><title><![CDATA[Implementing Chart.js in Angular]]></title><description><![CDATA[Data visualization is an essential part of web development, helping developers present complex data in a simple way. This series will focus on Chart.js, a popular JavaScript library for creating interactive charts, and show how to use it in an Angula...]]></description><link>https://blog.shaileshhb.in/implementing-chartjs-in-angular</link><guid isPermaLink="true">https://blog.shaileshhb.in/implementing-chartjs-in-angular</guid><category><![CDATA[chartjs]]></category><category><![CDATA[Angular]]></category><category><![CDATA[angular16]]></category><dc:creator><![CDATA[Shailesh B]]></dc:creator><pubDate>Wed, 20 Nov 2024 15:24:01 GMT</pubDate><content:encoded><![CDATA[<p>Data visualization is an essential part of web development, helping developers present complex data in a simple way. This series will focus on Chart.js, a popular JavaScript library for creating interactive charts, and show how to use it in an Angular application.</p>
<h4 id="heading-what-is-chartjs-and-why-use-it">What is Chart.js and Why Use It?</h4>
<p>Chart.js is an open-source JavaScript library that simplifies the creation of various types of charts, including bar, line, pie, radar, and scatter charts. Its flexibility, ease of use, and extensive customization options make it a preferred choice for developers aiming to add elegant visualizations to their projects.</p>
<p>Key features of Chart.js:</p>
<ul>
<li><p>Supports eight chart types out of the box.</p>
</li>
<li><p>Offers animation and interaction capabilities.</p>
</li>
<li><p>Highly customizable with plugins and configuration options.</p>
</li>
<li><p>Responsive by default, ensuring compatibility across devices.</p>
</li>
</ul>
<h4 id="heading-why-this-blog-series">Why This Blog Series?</h4>
<p>As a developer, I’ve often found it hard to get clear and detailed resources for Chart.js. While the documentation is useful, it sometimes lacks real-world examples or in-depth explanations. This inspired me to create this blog series—to share what I’ve learned and offer a practical guide for developers with similar challenges.</p>
<p>If you have tips, ideas, or improvements to share, I’d love for you to contribute. Together, we can build a helpful resource for the community and make life easier for developers.</p>
<p>You can find the full code and contributions on the repository: <a target="_blank" href="https://github.com/shaileshhb/angular-chartjs">angular-chartjs</a>.</p>
<h4 id="heading-setting-up-chhttpsgithubcomshaileshhbangular-chartjsartjs-in-angular"><a target="_blank" href="https://github.com/shaileshhb/angular-chartjs">Setting Up Ch</a>art.js in Angular</h4>
<p>This series will guide you step-by-step on integrating Chart.js into your Angular application using <strong>ng2-charts</strong>, an Angular wrapper for Chart.js. For this series, we will use the following versions:</p>
<ul>
<li><p><strong>Angular v16</strong>: The latest stable release with modern features and optimizations.</p>
</li>
<li><p><strong>Chart.js v4.4.0</strong>: A recent version of Chart.js with enhanced capabilities.</p>
</li>
<li><p><strong>ng2-charts v5.0.3</strong>: A powerful library that bridges Chart.js with Angular seamlessly.</p>
</li>
</ul>
<h4 id="heading-installation-steps-overview">Installation Steps Overview</h4>
<p>The first post in this series will cover:</p>
<ol>
<li><p>Installing Angular v16 (if not already set up).</p>
</li>
<li><p>Adding <strong>Chart.js</strong> and <strong>ng2-charts</strong> to your project:</p>
<pre><code class="lang-bash"> npm install chart.js@4.4.0 ng2-charts@5.0.3
</code></pre>
</li>
</ol>
<p>Stay tuned as we explore how to build, customize, and enhance charts in Angular using Chart.js. Let’s make data visualization easier, one step at a time!<br />This blog series was created with the help of ChatGPT to ensure clear and concise explanations.</p>
]]></content:encoded></item><item><title><![CDATA[Logging with Logrus: Streamlining MongoDB Integration]]></title><description><![CDATA[Logging plays a crucial role in modern programming by capturing errors, warnings, performance metrics, and debug messages. It serves as a means for our software to communicate with us, providing insights into its behavior.
In this blog, we'll learn h...]]></description><link>https://blog.shaileshhb.in/golang-logrus</link><guid isPermaLink="true">https://blog.shaileshhb.in/golang-logrus</guid><category><![CDATA[logrus]]></category><category><![CDATA[golang logger]]></category><category><![CDATA[golang-logrus]]></category><category><![CDATA[logging]]></category><category><![CDATA[golang]]></category><category><![CDATA[MongoDB]]></category><category><![CDATA[ logger]]></category><dc:creator><![CDATA[Shailesh B]]></dc:creator><pubDate>Sat, 27 Apr 2024 05:55:47 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1714190075788/f8e749c4-fd05-4468-be55-70e3b9cacb18.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Logging plays a crucial role in modern programming by capturing errors, warnings, performance metrics, and debug messages. It serves as a means for our software to communicate with us, providing insights into its behavior.</p>
<p>In this blog, we'll learn how to transfer our logs to MongoDB, enhancing our troubleshooting capabilities. By storing logs in MongoDB, we can easily analyze and debug issues, improving the efficiency of our software development process.</p>
<h2 id="heading-installing-logrushttpgithubcomsirupsenlogrus">Installing <a target="_blank" href="http://github.com/sirupsen/logrus">Logrus</a></h2>
<p>Logrus is a structured logger for Go (golang), completely API compatible with the standard library logger. Currently logrus is in <strong>maintenance-mode</strong> and there will be no new features added to this package, so if have not added logging package to your project then you should checkout <a target="_blank" href="https://github.com/uber-go/zap">Zap</a>, <a target="_blank" href="https://github.com/rs/zerolog">Zerolog</a>. They are popular logging packages in go. And if you still want to go ahead in installing <a target="_blank" href="http://github.com/sirupsen/logrus">Logrus</a> package then use the following command</p>
<pre><code class="lang-go"><span class="hljs-keyword">go</span> get github.com/sirupsen/logrus
</code></pre>
<h2 id="heading-basics-of-logrus">Basics of Logrus</h2>
<p>Now that we have installed the <a target="_blank" href="http://github.com/sirupsen/logrus">logrus</a> package let me show you few basic of how to use different methods for different purposes.</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> <span class="hljs-string">"github.com/sirupsen/logrus"</span>

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    logger := logrus.New()
    <span class="hljs-comment">// SetReportCaller will add the function name and filepath from where log was added</span>
    logger.SetReportCaller(<span class="hljs-literal">true</span>)

    logger.Info(<span class="hljs-string">"Log some information"</span>)
    logger.Error(<span class="hljs-string">"Log some error"</span>)
    logger.Println(<span class="hljs-string">"add some print statement"</span>)
    logger.Debug(<span class="hljs-string">"Log something for debugging purpose"</span>)
}
</code></pre>
<pre><code class="lang-powershell">go run main.go
time=<span class="hljs-string">"2024-04-27T10:24:13+05:30"</span> level=info msg=<span class="hljs-string">"Log some information"</span> func=main.main file=<span class="hljs-string">"D:/go-app/logger/logrus-blog.go:9"</span>
time=<span class="hljs-string">"2024-04-27T10:24:13+05:30"</span> level=error msg=<span class="hljs-string">"Log some error"</span> func=main.main file=<span class="hljs-string">"D:/go-app/logger/logrus-blog.go:10"</span>
time=<span class="hljs-string">"2024-04-27T10:24:13+05:30"</span> level=info msg=<span class="hljs-string">"add some print statement"</span> func=main.main file=<span class="hljs-string">"D:/go-app/logger/logrus-blog.go:11"</span>
</code></pre>
<p>These are few basic logging options that are provided by logrus. You can also set the logging level. This is helpful has you don't want to add debugging logs into your production server. You can set log level using <code>logger.SetLevel(level)</code> function. For additional information and all the functions provided by logrus you can head ahead to their documentation here: <a target="_blank" href="https://github.com/sirupsen/logrus?tab=readme-ov-file">https://github.com/sirupsen/logrus?tab=readme-ov-file</a></p>
<h2 id="heading-extracting-logs-into-file">Extracting logs into file</h2>
<p>Logrus supports different logging options like output logs into <code>console</code> or <code>file</code>. We can set the output by using <code>SetOutput</code> function as follow:</p>
<h3 id="heading-logging-into-stdout">Logging into stdout</h3>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"os"</span>

    <span class="hljs-string">"github.com/sirupsen/logrus"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    logger := logrus.New()
    logger.SetReportCaller(<span class="hljs-literal">true</span>)
    logger.SetLevel(logrus.InfoLevel)
    logger.SetOutput(os.Stdout)

    logger.Info(<span class="hljs-string">"Log some information"</span>)
    logger.Error(<span class="hljs-string">"Log some error"</span>)
    logger.Println(<span class="hljs-string">"add some print statement"</span>)
    logger.Debug(<span class="hljs-string">"Log something for debugging purpose"</span>)
}
</code></pre>
<h3 id="heading-logging-into-file">Logging into file</h3>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"os"</span>

    <span class="hljs-string">"github.com/sirupsen/logrus"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    logger := logrus.New()
    logger.SetReportCaller(<span class="hljs-literal">true</span>)
    logger.SetLevel(logrus.InfoLevel)

    logFile := <span class="hljs-string">"logger.log"</span>
    file, err := os.OpenFile(logFile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, <span class="hljs-number">0644</span>)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        logger.Error(err)
        <span class="hljs-built_in">panic</span>(err)
    }

    <span class="hljs-keyword">defer</span> file.Close()

    logger.SetOutput(file)

    logger.Info(<span class="hljs-string">"Log some information"</span>)
    logger.Error(<span class="hljs-string">"Log some error"</span>)
    logger.Println(<span class="hljs-string">"add some print statement"</span>)
    logger.Debug(<span class="hljs-string">"Log something for debugging purpose"</span>)
}
</code></pre>
<p>This will output the logs into logger.log file in the current directory.</p>
<h2 id="heading-using-hooks-to-extract-logs-into-mongodb">Using hooks to extract logs into MongoDB</h2>
<p>In Logrus, hooks are essentially interfaces that can be added to the logger to execute custom logic or side effects when log entries are made. A hook in Logrus only has to implement the <code>Hook</code> interface, which involves implementing two methods: <code>Levels()</code> and <code>Fire()</code>.</p>
<ul>
<li><p><code>Levels()</code>: Defines for which log levels this hook will be triggered.</p>
</li>
<li><p><code>Fire()</code>: Contains the logic that gets executed when a log of the specified levels is made.</p>
</li>
</ul>
<h3 id="heading-first-lets-connect-to-mongodb">First lets connect to MongoDB.</h3>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"context"</span>
    <span class="hljs-string">"fmt"</span>

    <span class="hljs-string">"github.com/sirupsen/logrus"</span>
    <span class="hljs-string">"go.mongodb.org/mongo-driver/bson"</span>
    <span class="hljs-string">"go.mongodb.org/mongo-driver/mongo"</span>
    <span class="hljs-string">"go.mongodb.org/mongo-driver/mongo/options"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    logger := logrus.New()
    logger.SetReportCaller(<span class="hljs-literal">true</span>)

    mongoClient := connectToMongo()
    <span class="hljs-keyword">defer</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">if</span> err := mongoClient.Disconnect(context.TODO()); err != <span class="hljs-literal">nil</span> {
            <span class="hljs-built_in">panic</span>(err)
        }
    }()
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">connectToMongo</span><span class="hljs-params">()</span> *<span class="hljs-title">mongo</span>.<span class="hljs-title">Client</span></span> {
    serverAPI := options.ServerAPI(options.ServerAPIVersion1)
    opts := options.Client().ApplyURI(<span class="hljs-string">"mongodb+srv://&lt;username&gt;:&lt;password&gt;@local-logger.vqrzbh1.mongodb.net/?retryWrites=true&amp;w=majority&amp;appName=local-logger"</span>).SetServerAPIOptions(serverAPI)

    client, err := mongo.Connect(context.TODO(), opts)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        <span class="hljs-built_in">panic</span>(err)
    }

    <span class="hljs-comment">// Send a ping to confirm a successful connection</span>
    <span class="hljs-keyword">if</span> err := client.Database(<span class="hljs-string">"admin"</span>).RunCommand(context.TODO(), bson.D{{<span class="hljs-string">"ping"</span>, <span class="hljs-number">1</span>}}).Err(); err != <span class="hljs-literal">nil</span> {
        <span class="hljs-built_in">panic</span>(err)
    }

    fmt.Println(<span class="hljs-string">"Pinged your deployment. You successfully connected to MongoDB!"</span>)
    <span class="hljs-keyword">return</span> client
}
</code></pre>
<p>This is what I am using to connect to mongo. This is very naive implementation you should change the implementation of connecting mongodb.</p>
<h3 id="heading-setup-hook">Setup Hook</h3>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"context"</span>
    <span class="hljs-string">"fmt"</span>
    <span class="hljs-string">"time"</span>

    <span class="hljs-string">"github.com/sirupsen/logrus"</span>
    <span class="hljs-string">"go.mongodb.org/mongo-driver/bson"</span>
    <span class="hljs-string">"go.mongodb.org/mongo-driver/mongo"</span>
    <span class="hljs-string">"go.mongodb.org/mongo-driver/mongo/options"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    logger := logrus.New()
    logger.SetReportCaller(<span class="hljs-literal">true</span>)

    mongoClient := connectToMongo()
    <span class="hljs-keyword">defer</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">if</span> err := mongoClient.Disconnect(context.TODO()); err != <span class="hljs-literal">nil</span> {
            <span class="hljs-built_in">panic</span>(err)
        }
    }()

    mongoHook := MongoHook{
        Client: mongoClient,
    }
}

<span class="hljs-keyword">type</span> MongoHook <span class="hljs-keyword">struct</span> {
    Client *mongo.Client
}

<span class="hljs-keyword">type</span> Log <span class="hljs-keyword">struct</span> {
    Timestamp time.Time
    Level     <span class="hljs-keyword">string</span>
    Message   <span class="hljs-keyword">string</span>
    File      <span class="hljs-keyword">string</span>
    Function  <span class="hljs-keyword">string</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(hook MongoHook)</span> <span class="hljs-title">Levels</span><span class="hljs-params">()</span> []<span class="hljs-title">logrus</span>.<span class="hljs-title">Level</span></span> {
    <span class="hljs-keyword">return</span> []logrus.Level{logrus.ErrorLevel}
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(hook MongoHook)</span> <span class="hljs-title">Fire</span><span class="hljs-params">(entry *logrus.Entry)</span> <span class="hljs-title">error</span></span> {
    logObj := Log{
        Timestamp: entry.Time,
        Level:     entry.Level.String(),
        Message:   entry.Message,
        File:      entry.Caller.File,
        Function:  entry.Caller.Function,
    }

    collection := hook.Client.Database(<span class="hljs-string">"&lt;your-databasename&gt;"</span>).Collection(<span class="hljs-string">"&lt;your-collectionname&gt;"</span>)
    collection.InsertOne(context.TODO(), logObj)
    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">connectToMongo</span><span class="hljs-params">()</span> *<span class="hljs-title">mongo</span>.<span class="hljs-title">Client</span></span> {
    serverAPI := options.ServerAPI(options.ServerAPIVersion1)

    opts := options.Client().ApplyURI(<span class="hljs-string">"mongodb+srv://&lt;username&gt;:&lt;password&gt;@local-logger.vqrzbh1.mongodb.net/?retryWrites=true&amp;w=majority&amp;appName=local-logger"</span>).SetServerAPIOptions(serverAPI)
    client, err := mongo.Connect(context.TODO(), opts)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        <span class="hljs-built_in">panic</span>(err)
    }

    <span class="hljs-comment">// Send a ping to confirm a successful connection</span>
    <span class="hljs-keyword">if</span> err := client.Database(<span class="hljs-string">"admin"</span>).RunCommand(context.TODO(), bson.D{{<span class="hljs-string">"ping"</span>, <span class="hljs-number">1</span>}}).Err(); err != <span class="hljs-literal">nil</span> {
        <span class="hljs-built_in">panic</span>(err)
    }

    fmt.Println(<span class="hljs-string">"Pinged your deployment. You successfully connected to MongoDB!"</span>)
    <span class="hljs-keyword">return</span> client
}
</code></pre>
<p>There is lot of stuff going on here now, let's cover everything step by step.</p>
<p><code>MongoHook</code>: This is struct whose methods will implement the <code>hook</code> interface of logrus package. I have added the mongo's client instance to it.</p>
<p><code>Levels()</code>: In the <code>Levels()</code> method I am returning the level of log which will be return into mongodb, has I don't want everything to go into mongo and troubleshooting becomes difficult.</p>
<p><code>Fire()</code>: This method is responsible for actually writing the logs into our MongoDB. The first thing I do is create a object which I want to write into database. The entry object provides various field which could be used for troubleshooting and you can choose them based on your choice. I have added few of them for demo purpose. Once the object is created I select the collection into which the logs are to be written. And finally the logs are written into the collection using <code>InsertOne()</code> function.</p>
<p>In this example I have used MongoDB to write my logs but we can use any entity to write our logs. We just need to implement the <code>Hook</code> interface of the logrus package and we are good to go.<br />Integrating Logrus with external entity in MongoDB for logging in your Golang applications can greatly enhance your troubleshooting and debugging processes. Start implementing these techniques in your projects today to experience the benefits firsthand. Happy logging!</p>
]]></content:encoded></item><item><title><![CDATA[Effortless CSV Parsing in Golang: A Hands-On Approach]]></title><description><![CDATA[Handling big CSV files can take a lot of time. But with Golang, it becomes much quicker. Unlike traditional methods, Golang makes the process more efficient, cutting down the time needed to deal with large CSV files. Golang achieves this efficiency t...]]></description><link>https://blog.shaileshhb.in/go-csv-parsing</link><guid isPermaLink="true">https://blog.shaileshhb.in/go-csv-parsing</guid><category><![CDATA[file-processing]]></category><category><![CDATA[go-csv-parsing]]></category><category><![CDATA[golang]]></category><category><![CDATA[CSV files]]></category><dc:creator><![CDATA[Shailesh B]]></dc:creator><pubDate>Sat, 03 Feb 2024 14:13:59 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1706964572524/7ee5d5fe-1a33-47a1-9dde-258fccecac36.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Handling big CSV files can take a lot of time. But with Golang, it becomes much quicker. Unlike traditional methods, Golang makes the process more efficient, cutting down the time needed to deal with large CSV files. Golang achieves this efficiency through its optimized performance and concurrent processing capabilities, helping developers simplify data tasks and improve overall speed. Using Golang turns the once burdensome job of managing extensive CSV datasets into a smoother and more time-saving process. It's a valuable tool for developers looking to increase productivity in data-heavy workflows.</p>
<h3 id="heading-1-setting-up-the-project-and-installing-the-required-packages">1. Setting up the project and Installing the required packages</h3>
<p>Let's first start with the setting up the go project. First we will initializes a new module in the current directory using the <code>go mod init moduleName</code> command. The module name can be your repository name. I am going to run the following command.</p>
<pre><code class="lang-bash">go mod init github.com/shaileshhb/go-file-parser
</code></pre>
<p>Once the command is executed two files will be create go.mod and go.sum. Now we will install the package that we are going to use for reading through the csv file.</p>
<pre><code class="lang-bash">go get -u github.com/gocarina/gocsv
</code></pre>
<p>Now we are ready to start writing our code. Create a main.go file where we will write our code.</p>
<h3 id="heading-2-opening-and-reading-a-csv-file">2. Opening and Reading a CSV File</h3>
<p>For reading the csv file we will have to open the file first. There are multiple approaches to open a file and we will be using <code>OpenFile</code> function from the <code>os</code> package</p>
<pre><code class="lang-go">    readFilePath := <span class="hljs-string">"process.csv"</span>

    <span class="hljs-comment">// Open the CSV readFile</span>
    readFile, err := os.OpenFile(readFilePath, os.O_RDONLY, os.ModePerm)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        <span class="hljs-built_in">panic</span>(err)
    }
    <span class="hljs-keyword">defer</span> readFile.Close()
</code></pre>
<p><code>OpenFile</code> method takes three parameters:</p>
<ul>
<li><p><code>readFilePath</code>: The path to the file you want to open.</p>
</li>
<li><p><code>os.O_RDONLY</code>: Flag indicating that the file should be opened for reading only.</p>
</li>
<li><p><code>os.ModePerm</code>: File permission mode, indicating that the file should have the default permissions for its type (e.g., 0666 for a regular file).</p>
</li>
</ul>
<p><code>OpenFile</code> function returns file and error object. Here since <code>OpenFile</code> returns us error we need to handle the error. Here the program will panic and stop the program execution but in real world scenario you will mostly return the error. And at last we need to close the file once our execution is completed.</p>
<h3 id="heading-3-defining-structs-to-match-csv-columns">3. Defining Structs to Match CSV Columns</h3>
<p>Since we are using <a target="_blank" href="http://github.com/gocarina/gocsv">github.com/gocarina/gocsv</a> package it has certain rule for defining the struct which will be filled while parsing the file.<br />When defining the struct each field has a tag called <code>csv</code> which will have the name of the column from the csv file.<br />For example: Say we have a csv with a column named 'Full Name'. For this we will define the struct as follow</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> User <span class="hljs-keyword">struct</span> {
    FullName <span class="hljs-keyword">string</span> <span class="hljs-string">`csv:"Full Name"`</span>
}
</code></pre>
<p>The csv file that I am going to use has following columns:<br />Organization Name, LinkedIn, Website, Total Funding Amount, Total Funding Amount Currency, Headquarters Location. So my struct will look something like this</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> Industry <span class="hljs-keyword">struct</span> {
    CompanyName                <span class="hljs-keyword">string</span> <span class="hljs-string">`csv:"Organization Name"`</span>
    LinkedIn                   <span class="hljs-keyword">string</span> <span class="hljs-string">`csv:"LinkedIn"`</span>
    Website                    <span class="hljs-keyword">string</span> <span class="hljs-string">`csv:"Website"`</span>
    TotalFundingAmount         <span class="hljs-keyword">int</span>    <span class="hljs-string">`csv:"Total Funding Amount"`</span>
    TotalFundingAmountCurrency <span class="hljs-keyword">string</span> <span class="hljs-string">`csv:"Total Funding Amount Currency"`</span>
    HeadquartersLocation       <span class="hljs-keyword">string</span> <span class="hljs-string">`csv:"Headquarters Location"`</span>
}
</code></pre>
<h3 id="heading-4-parsing-csv-data-into-structs-using-the-unmarshaltochan-function">4. Parsing CSV Data into Structs using the UnmarshalToChan() Function</h3>
<p><code>UnmarshalToChan</code> enables memory-efficient processing of large datasets by streaming records into channels instead of loading them all at once.<br />Calling the UnmarshalToChan is very simple. It just takes 2 arguments, first one is the file and second parameter the the channel. The channel has to be the type of the struct we just created.</p>
<pre><code class="lang-go">readChannel := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> Industry, <span class="hljs-number">1</span>)
err := gocsv.UnmarshalToChan(file, c)
<span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
    <span class="hljs-built_in">panic</span>(err)
}
</code></pre>
<p>Here we create <code>readChannel</code> where we will find our data. Once each record is read the entire data is pushed into the channel. And <code>file</code> is the file that we had opened previously. The <code>UnmarshalToChan</code> function returns the error so we need to handle it once again.<br />The package provides us option to set our csv reader wherein we can set few options for reading the csv file. It can be done as follows</p>
<pre><code class="lang-go">gocsv.SetCSVReader(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(r io.Reader)</span> <span class="hljs-title">gocsv</span>.<span class="hljs-title">CSVReader</span></span> {
    reader := csv.NewReader(r)
    reader.Comma = <span class="hljs-string">','</span>
    reader.FieldsPerRecord = <span class="hljs-number">-1</span>
    <span class="hljs-keyword">return</span> reader
})
</code></pre>
<p>Breakdown of the function is as follow<br /><code>gocsv.SetCSVReader</code><strong>:</strong> This function is used to set a custom CSV reader for parsing CSV data.</p>
<ol>
<li><p><code>func(r io.Reader) gocsv.CSVReader</code><strong>:</strong></p>
<ul>
<li><p>The function takes an <code>io.Reader</code> as an argument and returns a <code>gocsv.CSVReader</code>.</p>
</li>
<li><p>It defines a function literal (anonymous function) that specifies how the CSV reader should be configured.</p>
</li>
</ul>
</li>
<li><p><code>csv.NewReader(r)</code><strong>:</strong></p>
<ul>
<li>Creates a new CSV reader using the <code>csv</code> package from the Go standard library, and it takes an <code>io.Reader</code> as its parameter.</li>
</ul>
</li>
<li><p><code>reader.Comma = ','</code><strong>:</strong></p>
<ul>
<li>Sets the comma used for field separation in the CSV file. In this case, it's set to the standard comma (<code>,</code>).</li>
</ul>
</li>
<li><p><code>reader.LazyQuotes = true</code><strong>:</strong></p>
<ul>
<li>Configures the CSV reader to allow lazy quotes. Lazy quotes allow quotes in a field to span multiple lines.</li>
</ul>
</li>
<li><p><code>reader.FieldsPerRecord = -1</code><strong>:</strong></p>
<ul>
<li><p>FieldsPerRecord is the number of expected fields per record. If FieldsPerRecord is positive, Read requires each record to have the given number of fields. If FieldsPerRecord is 0, Read sets it to the number of fields in the first record, so that future records must have the same field count. If FieldsPerRecord is negative, no check is made and records may have a variable number of fields.</p>
</li>
<li><p>Sets <code>FieldsPerRecord</code> to -1, indicating that each record in the CSV file may have a different number of fields. This is useful for handling irregular CSV files where records might have varying numbers of fields.</p>
</li>
</ul>
</li>
</ol>
<p>The entire part up till here should something like this</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"encoding/csv"</span>
    <span class="hljs-string">"fmt"</span>
    <span class="hljs-string">"io"</span>
    <span class="hljs-string">"os"</span>
    <span class="hljs-string">"time"</span>

    <span class="hljs-string">"github.com/gocarina/gocsv"</span>
)

<span class="hljs-keyword">type</span> Industry <span class="hljs-keyword">struct</span> {
    CompanyName                <span class="hljs-keyword">string</span> <span class="hljs-string">`csv:"Organization Name"`</span>
    LinkedIn                   <span class="hljs-keyword">string</span> <span class="hljs-string">`csv:"LinkedIn"`</span>
    Website                    <span class="hljs-keyword">string</span> <span class="hljs-string">`csv:"Website"`</span>
    TotalFundingAmount         <span class="hljs-keyword">int</span>    <span class="hljs-string">`csv:"Total Funding Amount"`</span>
    TotalFundingAmountCurrency <span class="hljs-keyword">string</span> <span class="hljs-string">`csv:"Total Funding Amount Currency"`</span>
    HeadquartersLocation       <span class="hljs-keyword">string</span> <span class="hljs-string">`csv:"Headquarters Location"`</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    readChannel := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> Industry, <span class="hljs-number">1</span>)

    readFilePath := <span class="hljs-string">"process.csv"</span>

    <span class="hljs-comment">// Open the CSV readFile</span>
    readFile, err := os.OpenFile(readFilePath, os.O_RDONLY, os.ModePerm)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        <span class="hljs-built_in">panic</span>(err)
    }
    <span class="hljs-keyword">defer</span> readFile.Close()

    count := <span class="hljs-number">0</span>
    readFromCSV(readFile, readChannel)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">readFromCSV</span><span class="hljs-params">(file *os.File, c <span class="hljs-keyword">chan</span> Industry)</span></span> {
    gocsv.SetCSVReader(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(r io.Reader)</span> <span class="hljs-title">gocsv</span>.<span class="hljs-title">CSVReader</span></span> {
        reader := csv.NewReader(r)
        reader.Comma = <span class="hljs-string">','</span>
        reader.LazyQuotes = <span class="hljs-literal">true</span>
        reader.FieldsPerRecord = <span class="hljs-number">-1</span>
        <span class="hljs-keyword">return</span> reader
    })

    <span class="hljs-comment">// Read the CSV file into a slice of Record structs</span>
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        err := gocsv.UnmarshalToChan(file, c)
        <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
            <span class="hljs-built_in">panic</span>(err)
        }
    }()
}
</code></pre>
<p>Here I have extracted the code which is going to read from the csv file and <code>UnmarshalToChan</code> is wrapped inside anonymous go routine function, expect it everything looks the same.<br />Now the only missing part is reading from the channel which could be done using the for range as follow</p>
<pre><code class="lang-go"><span class="hljs-keyword">for</span> r := <span class="hljs-keyword">range</span> readChannel {
    fmt.Println(<span class="hljs-string">"========================================"</span>)
    fmt.Println(r)
    fmt.Println(<span class="hljs-string">"========================================"</span>)
    fmt.Println()
}
</code></pre>
<p>Here I am just printing out the record that has been read but in your case you can do process the record based on your requirement.</p>
<p>The entire code is below</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"encoding/csv"</span>
    <span class="hljs-string">"fmt"</span>
    <span class="hljs-string">"io"</span>
    <span class="hljs-string">"os"</span>
    <span class="hljs-string">"time"</span>

    <span class="hljs-string">"github.com/gocarina/gocsv"</span>
)

<span class="hljs-keyword">type</span> Industry <span class="hljs-keyword">struct</span> {
    CompanyName                <span class="hljs-keyword">string</span> <span class="hljs-string">`csv:"Organization Name"`</span>
    LinkedIn                   <span class="hljs-keyword">string</span> <span class="hljs-string">`csv:"LinkedIn"`</span>
    Website                    <span class="hljs-keyword">string</span> <span class="hljs-string">`csv:"Website"`</span>
    TotalFundingAmount         <span class="hljs-keyword">int</span>    <span class="hljs-string">`csv:"Total Funding Amount"`</span>
    TotalFundingAmountCurrency <span class="hljs-keyword">string</span> <span class="hljs-string">`csv:"Total Funding Amount Currency"`</span>
    HeadquartersLocation       <span class="hljs-keyword">string</span> <span class="hljs-string">`csv:"Headquarters Location"`</span>
}

<span class="hljs-comment">// 5.57ms -&gt; 600 records (read)</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    now := time.Now()
    readChannel := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> Industry, <span class="hljs-number">1</span>)

    readFilePath := <span class="hljs-string">"process.csv"</span>

    <span class="hljs-comment">// Open the CSV readFile</span>
    readFile, err := os.OpenFile(readFilePath, os.O_RDONLY, os.ModePerm)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        <span class="hljs-built_in">panic</span>(err)
    }
    <span class="hljs-keyword">defer</span> readFile.Close()

    count := <span class="hljs-number">0</span>
    readFromCSV(readFile, readChannel)

    <span class="hljs-comment">// Print the records</span>
    <span class="hljs-keyword">for</span> r := <span class="hljs-keyword">range</span> readChannel {
        fmt.Println(<span class="hljs-string">"========================================"</span>)
        fmt.Println(r)
        fmt.Println(<span class="hljs-string">"========================================"</span>)
        fmt.Println()

        count++
    }

    fmt.Println(time.Since(now), count)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">readFromCSV</span><span class="hljs-params">(file *os.File, c <span class="hljs-keyword">chan</span> Industry)</span></span> {
    gocsv.SetCSVReader(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(r io.Reader)</span> <span class="hljs-title">gocsv</span>.<span class="hljs-title">CSVReader</span></span> {
        reader := csv.NewReader(r)
        reader.Comma = <span class="hljs-string">','</span>
        reader.LazyQuotes = <span class="hljs-literal">true</span>
        reader.FieldsPerRecord = <span class="hljs-number">-1</span>
        <span class="hljs-keyword">return</span> reader
    })

    <span class="hljs-comment">// Read the CSV file into a slice of Record structs</span>
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        err := gocsv.UnmarshalToChan(file, c)
        <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
            <span class="hljs-built_in">panic</span>(err)
        }
    }()
}
</code></pre>
<p>I have pushed this code <a target="_blank" href="https://github.com/shaileshhb/go-file-parser">here</a> you can use it for reference. The repository also has a sample csv file which you can use for testing.<br />Here is the link for the package that I have used<br />Go package: <a target="_blank" href="https://pkg.go.dev/github.com/gocarina/gocsv">https://pkg.go.dev/github.com/gocarina/gocsv</a><br />Github: <a target="_blank" href="https://github.com/gocarina/gocsv">https://github.com/gocarina/gocsv</a></p>
<p>Thanks for reading through the blog. Please let me know if I have missed something or something that I have done is incorrect so that I could make those changes and learn from my mistakes.</p>
]]></content:encoded></item><item><title><![CDATA[Node.js ReadStream and WriteStream Explained: A Beginner's Journey]]></title><description><![CDATA[Welcome to the captivating world of streams in Node.js! In this blog, we will embark on an exciting journey to explore the realm of streams and delve into the powerful features of ReadStream and WriteStream.
Streams provide a flexible and efficient w...]]></description><link>https://blog.shaileshhb.in/nodejs-readstream-and-writestream</link><guid isPermaLink="true">https://blog.shaileshhb.in/nodejs-readstream-and-writestream</guid><category><![CDATA[Node.js]]></category><category><![CDATA[streams in nodejs]]></category><dc:creator><![CDATA[Shailesh B]]></dc:creator><pubDate>Sun, 11 Jun 2023 15:57:00 GMT</pubDate><content:encoded><![CDATA[<p>Welcome to the captivating world of streams in Node.js! In this blog, we will embark on an exciting journey to explore the realm of streams and delve into the powerful features of ReadStream and WriteStream.</p>
<p>Streams provide a flexible and efficient way to handle data in Node.js, especially when dealing with large files or processing data in real-time. Throughout this blog, we will cover essential techniques, along with real-world examples, to help you fully grasp the potential of ReadStream and WriteStream. By the end of this journey, you will have a solid understanding of how to leverage streams for optimized file operations and data manipulation in your Node.js applications. Get ready to unlock the power of streams and take your Node.js skills to the next level!</p>
<h2 id="heading-what-are-streams">What are Streams?</h2>
<p>Streams in Node.js are like pipelines that enable us to read and write data from various sources. They make it easy to read data from files, network connections, or other sources, and write it to files, network connections, or other destinations. Streams are especially handy when working with large amounts of data since they process it in smaller, manageable chunks instead of all at once. They also offer a convenient way to transfer data between different sources and destinations without manual handling.</p>
<p>In simpler words, the stream is the flow of data from one source to another.</p>
<h2 id="heading-types-of-stream">Types of stream</h2>
<p>There are four fundamental stream types within Node.js:</p>
<ul>
<li><p><a target="_blank" href="https://nodejs.org/api/stream.html#class-streamwritable"><code>Writable</code></a>: streams to which data can be written (for example, <a target="_blank" href="https://nodejs.org/api/fs.html#fscreatewritestreampath-options"><code>fs.createWriteStream()</code></a>).</p>
</li>
<li><p><a target="_blank" href="https://nodejs.org/api/stream.html#class-streamreadable"><code>Readable</code></a>: streams from which data can be read (for example, <a target="_blank" href="https://nodejs.org/api/fs.html#fscreatereadstreampath-options"><code>fs.createReadStream()</code></a>).</p>
</li>
<li><p><a target="_blank" href="https://nodejs.org/api/stream.html#class-streamduplex"><code>Duplex</code></a>: streams that are both <code>Readable</code> and <code>Writable</code> (for example, <a target="_blank" href="https://nodejs.org/api/net.html#class-netsocket"><code>net.Socket</code></a>).</p>
</li>
<li><p><a target="_blank" href="https://nodejs.org/api/stream.html#class-streamtransform"><code>Transform</code></a>: <code>Duplex</code> streams that can modify or transform the data as it is written and read (for example, <a target="_blank" href="https://nodejs.org/api/zlib.html#zlibcreatedeflateoptions"><code>zlib.createDeflate()</code></a>).</p>
</li>
</ul>
<p>This blog will explore the details of ReadStream and WriteStream, specifically highlighting their usage for file operations.</p>
<h2 id="heading-writestream-in-nodejs">WriteStream in NodeJS</h2>
<p>Writestream is a powerful stream type that enables developers to effortlessly write data to various destinations like files. With Writestream, you can seamlessly process substantial data volumes without concerns about memory consumption or performance bottlenecks. It offers a convenient solution for creating custom streams tailored to your application's needs.</p>
<h3 id="heading-creating-writestream">Creating WriteStream</h3>
<p>In this blog, we will explore the various events, properties, and methods offered by the WriteStream object.</p>
<p>To create a WriteStream in Node.js, we can use the <code>createWriteStream</code> function provided by the <code>fs</code> module. This function allows you to specify the filename you want to write to, and optionally, you can pass additional options as well.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">"fs"</span>);

<span class="hljs-comment">// immediately invoked function expression (IIFE)</span>
(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-comment">// creating writestream object</span>
    <span class="hljs-keyword">const</span> writeStream = fs.createWriteStream(<span class="hljs-string">"destination.txt"</span>)
})()
</code></pre>
<p>If the <code>destination.txt</code> file does not exist then write stream will create the file for you.</p>
<p>To write data into a file using the WriteStream object, you can utilize the <code>write</code> method.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">"fs"</span>);

<span class="hljs-comment">// immediately invoked function expression (IIFE)</span>
(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-comment">// creating writestream object</span>
    <span class="hljs-keyword">const</span> writeStream = fs.createWriteStream(<span class="hljs-string">"destination.txt"</span>)

    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> index = <span class="hljs-number">0</span>; index &lt; <span class="hljs-number">100000</span>; index++) {
        writeStream.write(<span class="hljs-string">`<span class="hljs-subst">${index}</span> `</span>)
    }
})()
</code></pre>
<p>To properly handle the completion of writing data, it is important to close the WriteStream. We can achieve this by utilizing the <code>close</code> method on the WriteStream object. By invoking <code>writeStream.close()</code>, you ensure that any pending operations are finalized, resources are released, and the WriteStream is gracefully closed.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> writeStream = fs.createWriteStream(<span class="hljs-string">"destination.txt"</span>)
writeStream.close()
</code></pre>
<p>When you call <code>writeStream.write(data)</code> in Node.js, the data is not immediately written directly into the file instead, it is initially written to a buffer. The buffer acts as a temporary storage area for the data. As more data is written, it accumulates in the buffer until it reaches its capacity.</p>
<p>Once the buffer is full, the entire contents of the buffer are written to the file. This process ensures that the data is efficiently written in chunks, reducing the number of disk operations and improving overall performance. It also helps in optimizing resource utilization, especially when dealing with large amounts of data.</p>
<p>By default, the buffer size for a WriteStream in Node.js is set to 16kB (16384 bytes), which is known as the <code>highWaterMark</code> value. However, you have the flexibility to modify this value according to your specific requirements.</p>
<p>To change the <code>highWaterMark</code> value when creating a WriteStream, you can use the <code>fs.createWriteStream</code> method and provide the desired value as an option. For example:</p>
<pre><code class="lang-javascript">fs.createWriteStream(<span class="hljs-string">"destination.txt"</span>, { <span class="hljs-attr">highWaterMark</span>: <span class="hljs-number">400</span> });
</code></pre>
<p>In this example, the highWaterMark value is set to 400 bytes. Adjusting the highWaterMark allows you to control the buffer size and optimize the balance between memory consumption and the frequency of disk writes.</p>
<p>To avoid memory issues, it's important to empty the buffer before writing more data to a file. When the buffer becomes full, any additional data will be stored in memory until the buffer is cleared. Accumulating data in the buffer without emptying it can consume excessive memory, especially with large datasets, leading to performance and stability problems.</p>
<p>To avoid memory issues, the WriteStream object's write methods return a boolean indicating if the buffer is full. By pausing or stopping the data source while writing, we can prevent excessive data accumulation. The WriteStream also emits a "drain" event when the buffer is emptied and the data is successfully written to the file. By listening to this event, we can know when it's safe to resume reading from the source.</p>
<p>Here is an example of the same</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">"fs"</span>);

<span class="hljs-comment">// immediately invoked function expression (IIFE)</span>
(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-comment">// creating writestream object</span>
    <span class="hljs-keyword">const</span> writeStream = fs.createWriteStream(<span class="hljs-string">"destination.txt"</span>)

  <span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>
  <span class="hljs-keyword">let</span> MAX_WRITE = <span class="hljs-number">10000000</span>

  <span class="hljs-keyword">const</span> writeToFile = <span class="hljs-function">() =&gt;</span> {

    <span class="hljs-keyword">while</span> (i &lt; MAX_WRITE) {

      <span class="hljs-keyword">if</span> (i === MAX_WRITE - <span class="hljs-number">1</span>) {
        <span class="hljs-keyword">return</span> writeStream.end(<span class="hljs-string">`<span class="hljs-subst">${i}</span> `</span>)
      }

      <span class="hljs-keyword">const</span> isWrite = writeStream.write(<span class="hljs-string">`<span class="hljs-subst">${i}</span> `</span>)

      <span class="hljs-keyword">if</span> (!isWrite) <span class="hljs-keyword">break</span>
      i++
    }
  }

  writeToFile()

  writeStream.on(<span class="hljs-string">"drain"</span>, <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Draining"</span>);
    writeToFile()
  })

  writeStream.on(<span class="hljs-string">"finish"</span>, <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"finish event emitted"</span>)
    writeStream.close()
  })

})()
</code></pre>
<p>In the given example, a WriteStream object is created to write data to a file called "destination.txt". The purpose is to write a sequence of numbers from 0 to 10,000,000 in that file. To accomplish this, a function named <code>writeToFile</code> is defined, which handles the writing process.</p>
<p>Inside the <code>writeToFile</code> function, two methods provided by the WriteStream object are utilized. The <code>write</code> method is used to write each number to the stream, ensuring that the data is stored in the file. This method also returns a boolean value that indicates whether the buffer used for writing is full or not.</p>
<p>When all the numbers have been written, the <code>end</code> method is called. This signifies that it is the final write operation, allowing the WriteStream to complete any remaining writes and finalize the writing process. Upon finishing, the WriteStream emits a special event called <code>finish</code>, which can be utilized to perform any necessary tasks, such as gracefully closing the WriteStream.</p>
<h2 id="heading-readstream-in-nodejs">ReadStream in NodeJS</h2>
<p>Understanding the ReadStream object is indeed simpler once you are familiar with the WriteStream. Creating a ReadStream object in Node.js follows a similar approach to creating a WriteStream object.</p>
<p>Here is an example for creating readstream object.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">"fs"</span>)
<span class="hljs-keyword">const</span> readStream = fs.createReadStream(<span class="hljs-string">"src.txt"</span>);
</code></pre>
<p>Similar to the WriteStream object, the ReadStream object provides a range of events, properties, and methods. One important property of the ReadStream object is the <code>highWaterMark</code>, which specifies the size of the internal buffer used for reading data. In the case of the ReadStream, the default value of the <code>highWaterMark</code> property is 64kB (65536 bytes).</p>
<p>Readstream reads data from a source and stores it in a buffer. To access and work with this data, we can utilize the <code>data</code> event provided by the ReadStream.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> readStream = fs.createReadStream(<span class="hljs-string">"../src.txt"</span>)

readStream.on(<span class="hljs-string">"data"</span>, <span class="hljs-function">(<span class="hljs-params">chunk</span>) =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(chunk.toString());
})
</code></pre>
<p>By registering a listener for the <code>data</code> event using the <code>on</code> method, we can specify a function to be executed whenever new data becomes available. In the given example, the listener function takes a parameter called <code>chunk</code>, which represents a portion of the data read from the source. It's important to note that the data received through the <code>data</code> event is in bytes. To work with this data as a string, we can use the <code>toString()</code> function to convert it into a readable format.</p>
<p>In the ReadStream object, there is an <code>end</code> event that is triggered when all the data has been read from the source. This event serves as an indicator that the reading process is complete.</p>
<p>To gracefully close the ReadStream after reading is finished, we can use the <code>close()</code> method provided by the ReadStream object. Invoking <code>readstream.close()</code> allows us to properly terminate the ReadStream and release any associated resources.</p>
<p>By closing the ReadStream, we ensure that all operations related to reading data are finalized, preventing any potential memory leaks or unnecessary resource usage.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> readStream = fs.createReadStream(<span class="hljs-string">"../src.txt"</span>)

readStream.on(<span class="hljs-string">"data"</span>, <span class="hljs-function">(<span class="hljs-params">chunk</span>) =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(chunk); <span class="hljs-comment">// output -&gt; &lt;Buffer 48 65 6c 6c 6f 20 57 6f 72 6c 64&gt;</span>
    <span class="hljs-built_in">console</span>.log(chunk.toString());
})

readStream.on(<span class="hljs-string">"end"</span>, <span class="hljs-function">() =&gt;</span> {
    readStream.close()
})
</code></pre>
<p>In the ReadStream object, data is not read from the source until the <code>data</code> event is triggered on the ReadStream. This means that the ReadStream waits for the <code>data</code> event to occur before actively fetching and processing data from the source.</p>
<p>The ReadStream in Node.js offers two essential methods: <code>pause</code> and <code>resume</code>. These methods play a vital role when we need to control the reading process by pausing and resuming it.</p>
<p>The <code>pause</code> method does exactly what its name suggests - it pauses the ReadStream. When we invoke this method, it temporarily halts the reading process, giving us the ability to stop consuming data from the source. This can be useful in various situations, such as when we want to prioritize other tasks or when we need to handle data at a slower pace.</p>
<p>On the other hand, the <code>resume</code> method allows us to restart the ReadStream after it has been paused. Once we call this method, the ReadStream continues from where it left off, ensuring that we don't miss any data from the source. This is particularly valuable when we're ready to consume the data again or when we've finished performing a task that required the ReadStream to be paused.</p>
<p>Here is a basic example of using all the concepts that I have covered in this blog.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> basicReadWrite = <span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">const</span> readStream = fs.createReadStream(<span class="hljs-string">"../src.txt"</span>)
  <span class="hljs-keyword">const</span> writeStream = fs.createWriteStream(<span class="hljs-string">"../dest2.txt"</span>)

  writeStream.on(<span class="hljs-string">"drain"</span>, <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"drain called"</span>);
    readStream.resume()
  })

  readStream.on(<span class="hljs-string">"data"</span>, <span class="hljs-function">(<span class="hljs-params">chunk</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (!writeStream.write(chunk)) {
      readStream.pause()
    }
  })

  readStream.on(<span class="hljs-string">"end"</span>, <span class="hljs-function">() =&gt;</span> {
    readStream.close()
  })
}

basicReadWrite()
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In conclusion, understanding the concepts of ReadStream and WriteStream in Node.js opens up a world of possibilities for handling large amounts of data efficiently. These powerful tools provide us with the means to read data from a file or stream it in real-time, as well as write data to a file or another stream. With their simple yet effective APIs, ReadStream and WriteStream make it easier than ever to handle data operations seamlessly.</p>
<p>By leveraging the capabilities of ReadStream, we can read data in chunks, enabling efficient memory usage and faster processing. This is particularly useful when dealing with large files or streaming data from external sources. The ability to handle data incrementally not only improves performance but also allows us to apply transformations or perform actions on the fly, making our applications more responsive and versatile. On the other hand, WriteStream empowers us to efficiently write data to a file or a stream, piece by piece. This is crucial when dealing with large data sets or when the data is being generated dynamically. By chunking the data and writing it in smaller portions, we can minimize memory consumption and optimize the overall process. Additionally, WriteStream provides convenient methods to handle events and ensure data integrity. When used together, ReadStream and WriteStream form a powerful duo, enabling seamless data processing pipelines. We can effortlessly read data from a ReadStream, perform any necessary operations or transformations, and then write the processed data to a WriteStream. This allows us to build efficient data pipelines, which are especially beneficial in scenarios where data needs to be transformed or transmitted in real-time.</p>
<p>Node.js has truly revolutionized the way we handle I/O operations, and ReadStream and WriteStream are prime examples of its capabilities. Whether you're working with large files, streaming data, or processing data in real-time, these features provide the flexibility and performance you need. So, embrace the power of ReadStream and WriteStream in Node.js, and unlock a whole new level of data processing capabilities. Start exploring their APIs, experimenting with different scenarios, and witness firsthand how they can enhance the efficiency, speed, and overall performance of your applications. Happy coding!</p>
]]></content:encoded></item></channel></rss>