<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Vincent Olagbemide</title>
    <description>The latest articles on Forem by Vincent Olagbemide (@vincentayorinde).</description>
    <link>https://forem.com/vincentayorinde</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1786496%2F8d1ab131-7933-4a6a-baf5-0fa4ec56dbce.jpg</url>
      <title>Forem: Vincent Olagbemide</title>
      <link>https://forem.com/vincentayorinde</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/vincentayorinde"/>
    <language>en</language>
    <item>
      <title>How Encrisoft Achieved 20x Speedup Without More Hardware</title>
      <dc:creator>Vincent Olagbemide</dc:creator>
      <pubDate>Fri, 03 Oct 2025 20:03:27 +0000</pubDate>
      <link>https://forem.com/vincentayorinde/how-encrisoft-achieved-20x-speedup-without-more-hardware-1657</link>
      <guid>https://forem.com/vincentayorinde/how-encrisoft-achieved-20x-speedup-without-more-hardware-1657</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;At &lt;a href="https://encrisoft.com" rel="noopener noreferrer"&gt;Encrisoft&lt;/a&gt;, we are genuinely excited about &lt;a href="https://usealerta.com" rel="noopener noreferrer"&gt;Alerta&lt;/a&gt;, our flexible instant alerting API that’s built to fit the unique needs of businesses. It has been amazing to see it in action, delivering over 80 million alerts so far, helping companies stay on top of things with timely, tailored notifications that make a real difference.&lt;/p&gt;

&lt;p&gt;But here’s the thing about growth: it’s a double-edged sword. More alerts means more value for customers, but it also means more pressure on the system. Over time, we started noticing signs of strain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dashboards that once loaded instantly began to lag.&lt;/li&gt;
&lt;li&gt;Queries that were lightning-fast slowed to a crawl.&lt;/li&gt;
&lt;li&gt;Uptime felt fragile because the database was under stress.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This article is a technical deep-dive into how we fixed that. You’ll see exactly how we:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Optimised PostgreSQL with the right indexes.&lt;/li&gt;
&lt;li&gt;Introduced Redis caching (the smart way).&lt;/li&gt;
&lt;li&gt;Achieved a 20× speedup without adding a single new server.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And most importantly, you’ll get the code, reasoning, and metrics so you can apply the same lessons in your own systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Scale Problem
&lt;/h2&gt;

&lt;p&gt;Let’s make it concrete.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Our &lt;code&gt;Alert&lt;/code&gt; table grew past &lt;strong&gt;80 million rows&lt;/strong&gt;, and it was still growing every month.&lt;/li&gt;
&lt;li&gt;Queries that powered critical dashboards, things like &lt;em&gt;“how many alerts in the last 30 days?”&lt;/em&gt; or &lt;em&gt;“show me transaction counts by type”&lt;/em&gt; started taking seconds instead of milliseconds.&lt;/li&gt;
&lt;li&gt;Even modest delays caused real pain. For businesses who rely on Alerta to see what’s happening right now, waiting two seconds for a dashboard to load is like driving with foggy glasses.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It was clear - the system wasn’t scaling gracefully.&lt;/p&gt;

&lt;h2&gt;
  
  
  Index to the Rescue
&lt;/h2&gt;

&lt;p&gt;We started by looking at the data. PostgreSQL has a wonderful feature: &lt;strong&gt;slow query logs&lt;/strong&gt;. By turning those on, we could see exactly which queries were dragging.&lt;/p&gt;

&lt;p&gt;The culprit? &lt;strong&gt;Sequential&lt;/strong&gt; scans. Our queries were walking through millions of rows because they didn’t have the right indexes to guide them.&lt;/p&gt;

&lt;p&gt;So we fixed that by creating &lt;strong&gt;composite indexes&lt;/strong&gt;, indexes tailored specifically to the queries users were actually running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-- For queries by user and time
CREATE INDEX CONCURRENTLY idx_alert_uid_createdat
  ON "Alert" ("userId", "createdAt" DESC);

-- For queries by user, type, and time
CREATE INDEX CONCURRENTLY idx_alert_uid_type_createdat
  ON "Alert" ("userId", "type", "createdAt" DESC);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The impact was immediate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dashboard queries that once took &lt;strong&gt;~2 seconds now returned in ~100 ms.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Database CPU usage during peak hours dropped by &lt;strong&gt;60%.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One small change, just two indexes, transformed performance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Redis Caching (Smart, Per-User)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Indexes solved the first problem, but we noticed another: users kept hitting the &lt;strong&gt;same dashboards again and again&lt;/strong&gt;. That meant the same queries were being re-run, even though the results hadn’t changed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The solution? &lt;strong&gt;Redis caching&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;But we didn’t just cache everything blindly. Instead, we cached at the per-user level, so each user got their own cached results, isolated from others.&lt;/p&gt;

&lt;p&gt;Here’s what the cache key looked like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;u:${userId}:alert_count:${filter}:${start}:${end}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here’s what that meant:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;u:${userId}&lt;/code&gt; → cache scoped to a specific user (no data leaks).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;alert_count&lt;/code&gt;→ the kind of query.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;${filter}:${start}:${end}&lt;/code&gt; → captures the parameters so the cache is precise.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With a short &lt;strong&gt;TTL (time-to-live) of 60 seconds,&lt;/strong&gt; users received nearly instant responses most of the time and had access to fresh data whenever needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  The results:
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Repeat queries came back in &amp;lt;50 ms.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Database load dropped even further, with &lt;strong&gt;80% fewer queries during peak.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Results Snapshot
&lt;/h2&gt;

&lt;p&gt;Here’s the before-and-after in one table:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Before Optimization&lt;/th&gt;
&lt;th&gt;After Optimization&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Dashboard load time&lt;/td&gt;
&lt;td&gt;~2 seconds&lt;/td&gt;
&lt;td&gt;~100–150 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Query load on database&lt;/td&gt;
&lt;td&gt;Very high&lt;/td&gt;
&lt;td&gt;70–80% lower&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CPU usage during peak&lt;/td&gt;
&lt;td&gt;80–90%&lt;/td&gt;
&lt;td&gt;30–40%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Alerta went from fragile at millions of rows to scalable at &lt;strong&gt;billions&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Personal Lessons as a Founder
&lt;/h2&gt;

&lt;p&gt;Looking back, here’s what this journey taught me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Measure before you optimise:&lt;/strong&gt;  We didn’t guess, we looked at slow query logs. That saved us from “fixing” the wrong thing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Index smart, not more:&lt;/strong&gt;  A single composite index delivered massive gains. Randomly adding indexes just bloats your database.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache with precision:&lt;/strong&gt;  User-level caching avoided the pitfalls of global caching (like data leaks or stale results).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scaling ≠ buying hardware:&lt;/strong&gt; Throwing servers at the problem would have been expensive and temporary. Engineering finesse gave us a long-term win.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;Performance is more than numbers; it’s about trust.&lt;/p&gt;

&lt;p&gt;When dashboards respond instantly, users feel confident. When queries drag, even for a second, trust erodes.&lt;/p&gt;

&lt;p&gt;At &lt;a href="https://encrisoft.com" rel="noopener noreferrer"&gt;Encrisoft&lt;/a&gt;, we restored that confidence in Alerta with nothing more than &lt;strong&gt;two indexes and smart caching.&lt;/strong&gt; The result was a &lt;strong&gt;20× speedup&lt;/strong&gt;, a database that runs cooler, and a platform ready for billions of alerts.&lt;/p&gt;

&lt;p&gt;And want to know the best part? These aren’t exotic tricks. Any engineering team can apply them. Measure, index smartly, cache wisely, and you’ll be amazed how far your existing infrastructure can go.&lt;/p&gt;

</description>
      <category>encrisoft</category>
      <category>alerta</category>
      <category>postgres</category>
      <category>indexing</category>
    </item>
    <item>
      <title>How to integrate Alerta into your Business</title>
      <dc:creator>Vincent Olagbemide</dc:creator>
      <pubDate>Tue, 01 Oct 2024 18:34:17 +0000</pubDate>
      <link>https://forem.com/vincentayorinde/how-to-integrate-alerta-into-your-business-2f4p</link>
      <guid>https://forem.com/vincentayorinde/how-to-integrate-alerta-into-your-business-2f4p</guid>
      <description>&lt;p&gt;In this post, we'll be creating a Slack channel called #finance where we'll send alerts on the transfer of funds and also send a reply to the same message after the transfer has been delivered.&lt;/p&gt;

&lt;p&gt;Prerequisite:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You should be a user of Slack, or have your own slack.com account, &lt;/li&gt;
&lt;li&gt;Create Channels for your notifications&lt;/li&gt;
&lt;li&gt;Create an Alerta account on (app.usealerta.com)&lt;/li&gt;
&lt;li&gt;Create a basic fintech app to plug in our alerts&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We are going to use a Sample Fintech app,&lt;/p&gt;

&lt;h2&gt;
  
  
  Create Slack Channel
&lt;/h2&gt;

&lt;p&gt;On Slack, let's assume we have a #finance team, #marketing team, and #security team.&lt;/p&gt;

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

&lt;p&gt;We'll go ahead and create a channel for each team and add each employee respectively.&lt;/p&gt;

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

&lt;p&gt;After this is done, you should have your channels created and ready to be integrated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create Alerta Account
&lt;/h2&gt;

&lt;p&gt;Create an account to get your API key from the Alerta App &lt;a href="https://app.usealerta.com/auth/create-account" rel="noopener noreferrer"&gt;Here&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ab0rurtq0b5hw5tmvwq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ab0rurtq0b5hw5tmvwq.png" alt="Create alerta account" width="800" height="665"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After successfully creating an account, save your API key, then switch to the &lt;code&gt;integrations&lt;/code&gt; page and select slack&lt;/p&gt;

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

&lt;p&gt;Select the channel you created from the Slack Oauth page. See below&lt;/p&gt;

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

&lt;p&gt;After successful integration, the channel should be integrated&lt;/p&gt;

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

&lt;p&gt;Note: This is how you can add any channels to your alerta account&lt;/p&gt;
&lt;h2&gt;
  
  
  Create the Sample Fintech App
&lt;/h2&gt;

&lt;p&gt;Now that we have successfully added our channels to the slack workspace.&lt;/p&gt;

&lt;p&gt;Let us see how to integrate Alerta into our code base. In case you want o see the full app, See &lt;a href="https://github.com/vincentayorinde/sample-fintech-app" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; to skip the step-by-step process  &lt;/p&gt;

&lt;p&gt;Also, you can watch the &lt;a href="https://www.youtube.com/watch?v=7D9O_BVwMH8" rel="noopener noreferrer"&gt;Youtube video&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Otherwise, let us continue&lt;/p&gt;

&lt;p&gt;Installing Nest JS and create a nest app using the following commands&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❯ npm i -g @nestjs/cli
❯ nest new sample-fintech-app
❯ cd sample-fintech-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's create the wallet service, module and controller&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❯ npm i @nestjs/config @nestjs/axios    
❯ nest g module wallet
❯ nest g service wallet
❯ nest g controller wallet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's create the alerta service, module and controller&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❯ nest g module alerta
❯ nest g service alerta
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Folder API Structure
&lt;/h2&gt;

&lt;p&gt;After running all the commands above, your sample finance app which should have this folder structure, feel free to remove the &lt;code&gt;.spec&lt;/code&gt; files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sample-wallet-app/
├── src/
│   ├── alerta/
│   │   ├── alerta.module.ts        # Module for alerta functions
│   │   └── alerta.service.ts       # for alerta external api calls
│   ├── wallet/
│   │   ├── wallet.controller.ts    # Controller handling wallet
│   │   ├── wallet.module.ts        # Module for Wallet functions
│   │   └── wallet.service.ts       # handling business logic
│   ├── app.module.ts               # Main application module
│   └── main.ts                     # Entry point for the application
├── node_modules/                   # Node dependencies
├── package.json                    # Dependencies
├── package-lock.json               # Lock file extracting version of our dependencies
├── tsconfig.json                   # TypeScript configuration file
└── .eslintrc.js                    # ESLint configuration (optional)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Add Wallet Services
&lt;/h2&gt;

&lt;p&gt;We'll create two functions in our &lt;code&gt;wallet.service.ts&lt;/code&gt; file. One to transfer funds and the second to withdraw funds.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Injectable } from '@nestjs/common';
import { AlertaService } from 'src/alerta/alerta.service';

@Injectable()
export class WalletService {
  constructor(private readonly alertaService: AlertaService) {}

  async walletTransfer(transferDto: {
    fromWalletID: string;
    toWalletID: string;
    amount: number;
  }) {
    const { fromWalletID, toWalletID, amount } = transferDto;
    const message = `Transferred ${amount} from wallet ${fromWalletID} to wallet ${toWalletID}`;

    return { message, success: true };
  }

  withdrawToBank(withDrawDto: {
    walletID: string;
    bankAccount: string;
    amount: number;
  }) {
    const { walletID, bankAccount, amount } = withDrawDto;
    const message = `Withdrawn ${amount} from wallet ${walletID} to bank account ${bankAccount}`;
    return { message, success: true };
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Add Wallet Controller
&lt;/h2&gt;

&lt;p&gt;We'll create two functions in our &lt;code&gt;wallet.controller.ts&lt;/code&gt; file. One initiates the transfer of funds and the second to withdraws funds.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Body, Controller, Post } from '@nestjs/common';
import { WalletService } from './wallet.service';

@Controller('wallet')
export class WalletController {
  constructor(private readonly walletService: WalletService) {}
  @Post('transfer')
  walletTransfer(
    @Body()
    transferDto: {
      fromWalletID: string;
      toWalletID: string;
      amount: number;
    },
  ) {
    return this.walletService.walletTransfer(transferDto);
  }

  @Post('withdraw')
  withdrawToBank(
    @Body()
    withDrawDto: {
      walletID: string;
      bankAccount: string;
      amount: number;
    },
  ) {
    return this.walletService.withdrawToBank(withDrawDto);
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have created our service and controller for the wallet service, lets add the module&lt;/p&gt;

&lt;p&gt;for &lt;code&gt;wallet.module.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Module } from '@nestjs/common';
import { WalletController } from './wallet.controller';
import { WalletService } from './wallet.service';

@Module({
  controllers: [WalletController],
  providers: [WalletService],
})
export class WalletModule {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Add Env Variables
&lt;/h2&gt;

&lt;p&gt;To secure our API keys, we'll add a &lt;code&gt;.env&lt;/code&gt; file and specify the api key and url there&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ALERTA_URL=place your api url
ALERTA_KEY=place your api key here
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Add Alerta Service
&lt;/h2&gt;

&lt;p&gt;After setting up our wallet and our env file, we'll create two functions in our &lt;code&gt;alerta.service.ts&lt;/code&gt; file. One to send the message and the second to reply to messages.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { HttpService } from '@nestjs/axios';
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { catchError, lastValueFrom } from 'rxjs';

@Injectable()
export class AlertaService {
  private alertaUrl = this.config.get('ALERTA_URL');
  private alertaKey = this.config.get('ALERTA_KEY');

  constructor(
    private config: ConfigService,
    private httpService: HttpService,
  ) {}
  async sendAlert(alertaDto: {
    message: string;
    channel: string;
    replyTo: boolean;
  }): Promise&amp;lt;any&amp;gt; {
    const { message, channel, replyTo } = alertaDto;

    try {
      const headers = {
        secretKey: `secret ${this.alertaKey}`,
        'Content-Type': 'application/json',
      };

      const res = await lastValueFrom(
        this.httpService
          .post(
            `${this.alertaUrl}/send`,
            { message, channel, replyTo },
            {
              headers,
            },
          )
          .pipe(
            catchError((error) =&amp;gt; {
              throw new HttpException(
                `Error fetching data from external API: ${error.message}`,
                HttpStatus.BAD_REQUEST,
              );
            }),
          ),
      );
      return res.data;
      return res.data;
    } catch (error) {
      return error;
    }
  }

  async replyAlert(alertaDto: {
    channelId: string;
    threadId: string;
    channelRef: string;
    message: string;
  }): Promise&amp;lt;any&amp;gt; {
    const { channelId, threadId, channelRef, message } = alertaDto;

    try {
      const headers = {
        secretKey: `secret ${this.alertaKey}`,
        'Content-Type': 'application/json',
      };

      const res = await lastValueFrom(
        this.httpService
          .post(
            `${this.alertaUrl}/reply`,
            { channelId, threadId, channelRef, message },
            { headers },
          )
          .pipe(
            catchError((error) =&amp;gt; {
              throw new HttpException(
                `Error processing data from API ${error}`,
                HttpStatus.BAD_REQUEST,
              );
            }),
          ),
      );
      return res.data;
    } catch (error) {}
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Add Alerta Module
&lt;/h2&gt;

&lt;p&gt;After setting up our alerta service, we'll update our module file &lt;code&gt;alerta.module.ts&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { HttpModule } from '@nestjs/axios';
import { Module } from '@nestjs/common';
import { AlertaService } from './alerta.service';
import { ConfigService } from '@nestjs/config';

@Module({
  imports: [HttpModule.register({ timeout: 5000, maxRedirects: 5 })],
  providers: [AlertaService, ConfigService],
  exports: [AlertaService],
})
export class AlertaModule {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are almost there,&lt;br&gt;
Before we test, lets us add the alerts to some places in our wallet service so we get the alerts when there is an operation.&lt;/p&gt;

&lt;p&gt;First lets update the &lt;code&gt;wallet.module.ts&lt;/code&gt; file to know our Alerta service&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
.
.
import { AlertaModule } from 'src/alerta/alerta.module';

@Module({
  imports: [AlertaModule],
  controllers: [WalletController],
  providers: [WalletService],
})
export class WalletModule {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;also, let us update the &lt;code&gt;app.module.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { WalletService } from './wallet/wallet.service';
import { WalletModule } from './wallet/wallet.module';
import { WalletController } from './wallet/wallet.controller';
import { AlertaService } from './alerta/alerta.service';
import { AlertaModule } from './alerta/alerta.module';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { HttpModule } from '@nestjs/axios';

@Module({
  imports: [
    ConfigModule.forRoot({ isGlobal: true }),
    WalletModule,
    AlertaModule,
    HttpModule,
  ],
  controllers: [AppController],
  providers: [AppService, ConfigService],
})
export class AppModule {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will notice that we added the &lt;code&gt;ConfigModule.forRoot({ isGlobal: true })&lt;/code&gt; this will ensure that all env config is availble and can be used app-wide.&lt;/p&gt;

&lt;h2&gt;
  
  
  Plug in Alerta into the codebase
&lt;/h2&gt;

&lt;p&gt;Now, in the &lt;code&gt;wallet.service.ts&lt;/code&gt;, let us assume we want to know when any user does a transfer or does a withdraw, we can send a message to the &lt;code&gt;#finance&lt;/code&gt; channel that we created and integrated earlier.&lt;br&gt;
Lets update our code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Injectable } from '@nestjs/common';
import { AlertaService } from 'src/alerta/alerta.service';

@Injectable()
export class WalletService {
// bring the service in by adding it the contructor
  constructor(private readonly alertaService: AlertaService) {}

  async walletTransfer(transferDto: {
    fromWalletID: string;
    toWalletID: string;
    amount: number;
  }) {
    const { fromWalletID, toWalletID, amount } = transferDto;
    const message = `Transferred ${amount} from wallet ${fromWalletID} to wallet ${toWalletID}`;

// Add the alert from the alerta service here. Also, because we want 
// to reply to it later, we'll set `replyTo` to true
    await this.alertaService.sendAlert({
      message,
      channel: 'finance',
      replyTo: true,
    });

    return { message, success: true };
  }
.
.
.

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

&lt;/div&gt;



&lt;p&gt;Now we can send messages to Slack in the channel. Let's assume we want to reply to the same message that was sent earlier. &lt;br&gt;
Usually, you can save the data from the response after you have sent a message so the message can be replied to in the future.&lt;/p&gt;

&lt;p&gt;The data needed for the reply is &lt;code&gt;threadId&lt;/code&gt;, &lt;code&gt;channelId&lt;/code&gt; and &lt;code&gt;channelRef&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  To add the Reply
&lt;/h2&gt;

&lt;p&gt;Before adding the reply feature, we have to mention the @Alerta App in the channel&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Injectable } from '@nestjs/common';
import { AlertaService } from 'src/alerta/alerta.service';

@Injectable()
export class WalletService {
  constructor(private readonly alertaService: AlertaService) {}

  async walletTransfer(transferDto: {
    fromWalletID: string;
    toWalletID: string;
    amount: number;
  }) {
    const { fromWalletID, toWalletID, amount } = transferDto;
    const message = `Transferred ${amount} from wallet ${fromWalletID} to wallet ${toWalletID}`;

    const alertRes = await this.alertaService.sendAlert({
      message,
      channel: 'finance',
      replyTo: true,
    });

// triggering the reply
    setTimeout(() =&amp;gt; {
      this.alertaService.replyAlert({
        message: 'Transfer delivered!',
        threadId: alertRes.data.replyData.threadId,
        channelId: alertRes.data.replyData.channelId,
        channelRef: alertRes.data.replyData.channelRef,
      });
    }, 5000);

    return { message, success: true };
  }

  withdrawToBank(withDrawDto: {
    walletID: string;
    bankAccount: string;
    amount: number;
  }) {
    const { walletID, bankAccount, amount } = withDrawDto;
    const message = `Withdrawn ${amount} from wallet ${walletID} to bank account ${bankAccount}`;
    return { message, success: true };
  }
}

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

&lt;/div&gt;



&lt;p&gt;Now let's test our implementation&lt;/p&gt;

&lt;p&gt;Test transfer&lt;/p&gt;

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

&lt;h2&gt;
  
  
  The Slack Message
&lt;/h2&gt;

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

&lt;h2&gt;
  
  
  The Reply to the existing message
&lt;/h2&gt;

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

&lt;p&gt;In conclusion, we created a Slack channel called #finance where we'll send updates messages on the transfer of funds and also send a reply to the same message after the transfer has been delivered.&lt;/p&gt;

&lt;p&gt;Thank you.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>alerta</category>
      <category>slack</category>
      <category>nestjs</category>
    </item>
  </channel>
</rss>
