<?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>DEV Community: Chinedu Ogama</title>
    <description>The latest articles on DEV Community by Chinedu Ogama (@eduvin).</description>
    <link>https://dev.to/eduvin</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%2F295338%2Fb0214b06-1a1e-41d3-af01-b2d411b0f957.png</url>
      <title>DEV Community: Chinedu Ogama</title>
      <link>https://dev.to/eduvin</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/eduvin"/>
    <language>en</language>
    <item>
      <title>Send an Excel File as an attachment via Postmark Template API in NestJS.</title>
      <dc:creator>Chinedu Ogama</dc:creator>
      <pubDate>Mon, 24 Mar 2025 14:16:39 +0000</pubDate>
      <link>https://dev.to/eduvin/send-an-excel-file-as-an-attachment-via-postmark-template-api-in-nestjs-2cpb</link>
      <guid>https://dev.to/eduvin/send-an-excel-file-as-an-attachment-via-postmark-template-api-in-nestjs-2cpb</guid>
      <description>&lt;p&gt;Data is important to a business's success as it provides valuable insights into customer behaviour and preferences. This empowers organizations by helping them better understand their customers, influence product decisions, and identify areas for improvement. Data export is a common product feature that helps provide the required data today. Excel files are the most commonly exported file type.&lt;/p&gt;

&lt;p&gt;This article will walk you through how to use &lt;code&gt;NestJS&lt;/code&gt; to handle an export data request, excel file generation and email delivery. The request generates an Excel file that is sent as an email attachment through the &lt;code&gt;Postmark&lt;/code&gt; template API. The article assumes you have an existing Nestjs application with bull queues already configured and a postmark account with API access.&lt;/p&gt;

&lt;p&gt;The first step is to install the required packages which are &lt;code&gt;exceljs&lt;/code&gt; and &lt;code&gt;postmark&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;npm install exceljs
npm install postmark
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create your report dto &lt;code&gt;report.dto.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 { IsNotEmpty } from 'class-validator';

export class ReportDto {
  @IsNotEmpty()
  startDate: Date;

  @IsNotEmpty()
  endDate: Date;
}

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

&lt;/div&gt;



&lt;p&gt;Update your controller to handle the request and response.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//report.controller.ts

  @Post('/')
  @HttpCode(HttpStatus.OK)
  async getReport(@Body() payload: ReportDto) {
    const response = await this.service.createReport(payload);
    return { message: response };
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next step is to update the service and create the &lt;code&gt;createReport&lt;/code&gt; function that is called on the &lt;code&gt;report.controller.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;//report.service.ts

import { Injectable } from '@nestjs/common';
import { ReportDto } from './dto/report.dto';
import { InjectQueue } from '@nestjs/bull';
import { Queue } from 'bull';

@Injectable()
export class ReportService {
  constructor(@InjectQueue('report') private queue: Queue) {}

  async createReport(payload: ReportDto) {
    const startDate = new Date(payload.startDate);
    const endDate = new Date(payload.endDate);
    startDate.setHours(0, 0, 0, 0);
    endDate.setHours(23, 59, 59, 999);

    const queueData = {
      startDate: startDate,
      endDate: endDate,
    };

    await this.queue.add('report.generate-user-report', queueData);
    return 'Your report is on its way to your inbox';
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code above converts the payload dates to the start of the day for the startDate and the end of the day for endDate. The converted dates are sent as data for the queue.&lt;/p&gt;

&lt;p&gt;Here is the consumer class that handles the report queue.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//report.consumer.ts
import {
  OnQueueActive,
  OnQueueCompleted,
  Process,
  Processor,
} from '@nestjs/bull';
import { ConfigService } from '@nestjs/config';
import { Job } from 'bull';
import * as ExcelJS from 'exceljs';
import * as Postmark from 'postmark';
import { PrismaService } from '../prisma/prisma.service';

@Processor('report')
export class ReportConsumer {
  private readonly client = new Postmark.ServerClient(
    this.configService.getOrThrow&amp;lt;string&amp;gt;('POSTMARK_SECRET'),
  );

  constructor(
    private readonly prismaService: PrismaService,
    private readonly configService: ConfigService,
  ) {}

  @OnQueueActive()
  onActive(job: Job) {
    console.log(`Processing job ${job.id} of type ${job.name}`);
  }

  @OnQueueCompleted()
  onCompleted(job: Job) {
    console.log(`Processed job ${job.id} of type ${job.name}`);
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the &lt;code&gt;generateUserReport&lt;/code&gt; to handle the report generation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//report.consumer.ts

 @Process('report.generate-user-report')
  async generateUserReport(job: {
    data: {
      startDate: Date;
      endDate: Date;
    };
  }) {
    const { startDate, endDate } = job.data;
    // get registered user within the provided time frame
    const users = await this.prismaService.user.findMany({
      select: {
        firstname: true,
        lastname: true,
        email: true,
        createdAt: true,
      },
      where: {
        createdAt: {
          gt: startDate,
          lte: endDate,
        },
      },
    });

    //generate rows for the excel sheet
    const userRows = users.map((user) =&amp;gt; {
      return {
        firstname: user.firstname,
        lastname: user.lastname,
        email: user.email,
        signup_date: user.createdAt,
      };
    });

    // create excel workbook and add headers
    const workbook = new ExcelJS.Workbook();
    const worksheet = workbook.addWorksheet('Users Report');
    worksheet.columns = [
      { header: 'firstname', key: 'firstname', width: 32 },
      { header: 'lastname', key: 'lastname', width: 32 },
      { header: 'Email', key: 'email', width: 30 },
      { header: 'Signup Date', key: 'signup_date', width: 10 },
    ];

    worksheet.addRows(userRows);

    // writes the workbook to a buffer
    const report = await workbook.xlsx.writeBuffer();

    // send the mail to user
    await this.sendMail(report, startDate, endDate);
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next step is to add the function for sending the email.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async sendMail(report: ExcelJS.Buffer, startDate: Date, endDate: Date) {
    try {
      // data for the postmark template
      const emailData = {
        startDate: new Date(startDate).toISOString().split('T')[0],
        endDate: new Date(endDate).toISOString().split('T')[0],
      };

      // send an email with the generated sheet as an attachment
      const sendMail = await this.client.sendEmailWithTemplate({
        From: this.configService.getOrThrow&amp;lt;string&amp;gt;('ADMIN_EMAIL'),
        To: 'test@dev.to',
        TemplateAlias: this.configService.getOrThrow&amp;lt;string&amp;gt;(
          'POSTMARK_TEMPLATE__REPORT',
        ),
        TemplateModel: emailData,
        Attachments: [
          {
            Name: 'user_report.xlsx',
            ContentID: new Date().toString(),
            Content: Buffer.from(report).toString('base64'),
            ContentType:
              'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
          },
        ],
      });

      if (sendMail.ErrorCode !== 0) {
        throw new Error('Message failed to deliver');
      }
    } catch (error) {
      console.log(error?.message || error);
    }
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The final step is to update your env file with the required variables.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POSTMARK_TEMPLATE__REPORT=test-user-report
POSTMARK_SECRET=********-****-****-****-*******
ADMIN_EMAIL=no-reply@dev.to
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The postmark template value is the alias of the templatecreated on Postmark, while the secret is the Postmark API key.&lt;/p&gt;

&lt;p&gt;This article gives a top-level implementation for the feature. It can always be optimized to improve performance and meet specific needs.&lt;/p&gt;

</description>
      <category>nestjs</category>
      <category>typescript</category>
      <category>api</category>
    </item>
    <item>
      <title>How to deploy a Laravel/Vue App to Heroku</title>
      <dc:creator>Chinedu Ogama</dc:creator>
      <pubDate>Wed, 01 Jan 2020 08:15:13 +0000</pubDate>
      <link>https://dev.to/eduvin/how-to-deploy-a-laravel-vue-app-to-heroku-4kmg</link>
      <guid>https://dev.to/eduvin/how-to-deploy-a-laravel-vue-app-to-heroku-4kmg</guid>
      <description>&lt;p&gt;In this article, I will be showing you how to deploy a Laravel/Vue application to Heroku; a container-based cloud Platform as a Service (PaaS), which developers use to deploy, manage and scale modern apps.&lt;/p&gt;

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

&lt;p&gt;•  PHP and Laravel Knowledge &lt;br&gt;
•  Heroku User Account&lt;br&gt;
•  Heroku CLI (Download &lt;a href="https://devcenter.heroku.com/articles/heroku-cli" rel="noopener noreferrer"&gt;here&lt;/a&gt;)&lt;br&gt;
•  Git (get git &lt;a href="https://git-scm.com/book/en/v2/Getting-Started-Installing-Git" rel="noopener noreferrer"&gt;here&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;This article assumes that you have an existing Laravel/Vue application on your local server which is ready for deployment&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Initialize a git&lt;/strong&gt;&lt;br&gt;
Initialize a git repository in your current working project directory  with &lt;code&gt;git init&lt;/code&gt; command &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Create a Procfile&lt;/strong&gt;&lt;br&gt;
In your project directory create a &lt;b&gt;Procfile&lt;/b&gt; without an extension and add this line &lt;code&gt;web: vendor/bin/heroku-php-apache2 public/ &lt;/code&gt;. The &lt;b&gt;Procfile&lt;/b&gt; can also be created and updated through the terminal, to do this,  run &lt;code&gt;echo "web: vendor/bin/heroku-php-apache2 public/" &amp;gt; Procfile&lt;/code&gt; command on your terminal &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Create a new application on Heroku&lt;/strong&gt;&lt;br&gt;
In other to create a new application on Heroku where you can push your application to, use the &lt;code&gt;heroku create&lt;/code&gt; command. When this is done a random name will be automatically chosen for your application. To change this name use &lt;code&gt;heroku apps:rename newAppName &lt;/code&gt; command. Replace “newAppName” with your preferred new name.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4: Enable node.js&lt;/strong&gt; &lt;br&gt;
You need to enable node.js in other to run commands like npm install and npm production. To do this you need to add &lt;code&gt;heroku/nodejs&lt;/code&gt; build pack using &lt;code&gt;heroku buildpacks:add heroku/nodejs&lt;/code&gt; command. With this, the node dependencies in your &lt;code&gt;package.json&lt;/code&gt; file will be installed on deployment but it won’t install any of your devDependencies. To solve this you need to set an environment variable to tell Heroku to install all dependencies including devDependencies using &lt;code&gt;heroku config:set NPM_CONFIG_PRODUCTION=false&lt;/code&gt; command then add &lt;code&gt;postinstall&lt;/code&gt; in &lt;code&gt;package.json&lt;/code&gt; scripts&lt;br&gt;
&lt;code&gt;"scripts": {&lt;br&gt;
     "postinstall": "npm run prod"&lt;br&gt;
  }&lt;br&gt;
&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 5: Setup a Laravel encryption key&lt;/strong&gt;&lt;br&gt;
To set up your Laravel encryption key copy the &lt;code&gt;APP_KEY&lt;/code&gt; environment value from your &lt;code&gt;.env&lt;/code&gt; file and run &lt;code&gt;heroku config:set APP_KEY=”Your app key”&lt;/code&gt; or you can generate a new one and set it as your new key using &lt;code&gt;heroku config:set APP_KEY=$(php artisan --no-ansi key:generate --show)&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 6: Push to Heroku&lt;/strong&gt;&lt;br&gt;
Commit the current state of your application with git  and push to Heroku with &lt;code&gt;git push heroku master&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 7: Ensure that your application is using the right Heroku buildpacks&lt;/strong&gt;&lt;br&gt;
You need to ensure that your application is using the right buildpacks. To do this run the &lt;code&gt;heroku buildpacks&lt;/code&gt; command. If you have &lt;code&gt;heroku/php&lt;/code&gt; and &lt;code&gt;heroku/nodejs&lt;/code&gt; listed you are good to go. &lt;/p&gt;

&lt;p&gt;If you can’t find any, run  &lt;code&gt;heroku buildpacks:add [‘missing build’]&lt;/code&gt; command, replace the [‘missing build’] with the buildpack you wish to install and push to Heroku.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 8: Your app should be up and running. To view it,  navigate to the address in your browser&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To set the environment variables for your application you can do that using your terminal with the &lt;code&gt;heroku config:set VAR_NAME=VAR_VALUE&lt;/code&gt; command or through your dashboard in the settings tab, click on Reveal config vars to see and set environment variables.&lt;/p&gt;

&lt;p&gt;Heroku provides you the option to use postgres sql free. To use this run the command:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;heroku addons:create heroku-postgresql:hobby-dev&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;Set &lt;code&gt;DB_CONNECTION&lt;/code&gt; to pgsql through your dashboard in the settings tab, click on Reveal config vars to see environment variable.&lt;/p&gt;

&lt;p&gt;To get DB credentials for your application click on the &lt;br&gt;
&lt;b&gt;Heroku Postgres  Hobby Dev&lt;/b&gt; installed addon on the overview tab on your dashboard, this will open a new browser tab. The DB credentials can be found through the settings tab of the new browser tab.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Note:&lt;/b&gt;To run your regular &lt;b&gt;artisan&lt;/b&gt; or &lt;b&gt;npm&lt;/b&gt; commands on heroku, precede all statements with &lt;code&gt;heroku run&lt;/code&gt; e.g. &lt;code&gt;heroku run php artisan storage:link&lt;/code&gt; or &lt;code&gt;heroku run npm install&lt;/code&gt;&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>vue</category>
      <category>heroku</category>
      <category>php</category>
    </item>
  </channel>
</rss>
