<?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: Lakindu Hewawasam</title>
    <description>The latest articles on DEV Community by Lakindu Hewawasam (@lakinduhewawasam).</description>
    <link>https://dev.to/lakinduhewawasam</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%2F804981%2Fc60a5995-b900-4943-9d50-6962af880a63.jpeg</url>
      <title>DEV Community: Lakindu Hewawasam</title>
      <link>https://dev.to/lakinduhewawasam</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lakinduhewawasam"/>
    <language>en</language>
    <item>
      <title>How To Build a Node.js Express App in Under 5 Minutes?</title>
      <dc:creator>Lakindu Hewawasam</dc:creator>
      <pubDate>Mon, 04 Dec 2023 18:07:22 +0000</pubDate>
      <link>https://dev.to/lakinduhewawasam/how-to-build-a-nodejs-express-app-in-under-5-minutes-104d</link>
      <guid>https://dev.to/lakinduhewawasam/how-to-build-a-nodejs-express-app-in-under-5-minutes-104d</guid>
      <description>&lt;p&gt;As we move into 2024, the ideology behind faster development times has never been more critical. But, when building a Node.js Express app, that hasn't always been the case. In my experience, it usually takes a long time to bootstrap an Express API. This includes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Creating a boilerplate NPM project using &lt;code&gt;npm init&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; Installing the required libraries - Express, Nodemon, Body Parser, and TypeScript to configure your Express Server&lt;/li&gt;
&lt;li&gt; Setting up ESLint and creating the perfect ruleset for the project&lt;/li&gt;
&lt;li&gt; Setting up Module Federation - If you want to share code between your Node.js and Express.js microservices.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This means you're spending more time on things that aren't adding value to your software. You'd be better off building a feature, but instead, you're configuring a linter or a build command.&lt;/p&gt;

&lt;h1&gt;
  
  
  How Can We Build Faster Express Apps?
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;The short answer is to treat your service as a component&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It would help if you had a toolkit that lets you build your Express.js API as components where each component is designed, built, tested, and versioned in its isolated environment &lt;strong&gt;without you doing any configuration.&lt;/strong&gt; Doing so allows you to start by building features without focusing on any operational needs.&lt;/p&gt;

&lt;p&gt;To do this, you'd need to break your app down into two areas:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; The actual app&lt;/li&gt;
&lt;li&gt; The services&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Your app and service could be treated as two components, where your service component holds the feature, while the app component provides a runtime to execute your service. By creating an independence of this nature, you can improve your development cycle time by testing your services without spinning up the app.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;And one such tool that I've found that supports this component-driven approach is &lt;a href="https://bit.dev"&gt;Bit&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ocCJtPar--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.writergate.com/uploads/dc64a919-1200-42d3-90c6-9bc96dd9f4fa/kFCAcsWVdl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ocCJtPar--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.writergate.com/uploads/dc64a919-1200-42d3-90c6-9bc96dd9f4fa/kFCAcsWVdl.png" alt="" width="800" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Figure: Bit.dev&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Bit is a next-generation build system for developing composable software. It let's you build your components in an isolated environment and uses services like Ripple CI to propagate changes up your component tree. For example, consider the diagram depicted below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--K4WlYD4y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.writergate.com/uploads/dc64a919-1200-42d3-90c6-9bc96dd9f4fa/-dS9KGIVoX.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--K4WlYD4y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.writergate.com/uploads/dc64a919-1200-42d3-90c6-9bc96dd9f4fa/-dS9KGIVoX.png" alt="" width="800" height="444"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Figure: A sample component-driven API&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's say that everything we've defined above is a component. And let's say that you change the logic on &lt;code&gt;GetBlogById&lt;/code&gt; to return an error if Blog is not found.&lt;/p&gt;

&lt;p&gt;If you make this change, it should automatically reflect the change in your &lt;code&gt;BlogBackend&lt;/code&gt; route, right?&lt;/p&gt;

&lt;p&gt;Well, this is what Bit does. It keeps track of the dependencies that your component uses, and if it detects a change, it propagates it up the tree, creating a Ripple effect, thus deploying the entire tree again to ensure that you're running on the latest change.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Sounds interesting. This is how I build my apps daily, and I thought I'd share my experience with it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Building an Express.js App with Bit
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Step 01: Pre-requisites
&lt;/h2&gt;

&lt;p&gt;To build an Express.js app in under 5 minutes, you’ll need to install the &lt;a href="https://bit.dev/docs/getting-started/installing-bit/installing-bit/"&gt;Bit CLI&lt;/a&gt; using the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx @teambit/bvm install

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

&lt;/div&gt;


&lt;p&gt;Next, you’ll need to verify that the CLI has been installed successfully by running the command:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bit --version

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

&lt;/div&gt;


&lt;p&gt;If you’ve installed Bit correctly, you should see the screenshot below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jVx02dT6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.writergate.com/uploads/dc64a919-1200-42d3-90c6-9bc96dd9f4fa/DfO1h_0Ca4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jVx02dT6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.writergate.com/uploads/dc64a919-1200-42d3-90c6-9bc96dd9f4fa/DfO1h_0Ca4.png" alt="" width="368" height="47"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Figure: Installing Bit Correctly&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Next, you'll need to create an account on &lt;a href="https://bit.cloud/"&gt;Bit Cloud&lt;/a&gt;. After you've created your account, go ahead and &lt;a href="https://bit.cloud/create-scope"&gt;create a scope&lt;/a&gt; with the name &lt;strong&gt;blog:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1qsO0PuN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.writergate.com/uploads/dc64a919-1200-42d3-90c6-9bc96dd9f4fa/nLmAZUed0m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1qsO0PuN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.writergate.com/uploads/dc64a919-1200-42d3-90c6-9bc96dd9f4fa/nLmAZUed0m.png" alt="" width="693" height="504"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Figure: Creating a scope on Bit Cloud&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A Scope is a remote server in which your components live. Think of it like a GitHub repository, but it's not language-oriented. You can store anything in a scope and let people contribute to it.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;a href="https://bit.dev/reference/reference/scope/scope-overview/" rel="noopener noreferrer"&gt;
      bit.dev
    &lt;/a&gt;
&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 02: Creating the Express.js App
&lt;/h2&gt;

&lt;p&gt;Next, in a new directory, initialize a Bit workspace using the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bit init

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

&lt;/div&gt;


&lt;p&gt;By doing so, you'll create a space for local development. You'll see a file - &lt;code&gt;workspace.jsonc&lt;/code&gt;. Open that file and update the &lt;code&gt;defaultScope&lt;/code&gt; parameter with the following - &lt;code&gt;&amp;lt;&amp;lt;YOUR-USERNAME&amp;gt;&amp;gt;.&amp;lt;&amp;lt;SCOPE-NAME&amp;gt;&amp;gt;&lt;/code&gt;. After you do so, it should look like the snippet I've attached below:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"defaultScope": "dummyorg.blog",

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

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;Replace this with your username, and scope we created in Step 01.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Notice that your workspace isn't tied to a programming runtime. So, you can create an Express API and immediately create a React/ Angular/ React Native/ Vue.js component to consume the API as well.&lt;/p&gt;

&lt;p&gt;Next, let's create an Express App. This can be done in one command:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bit create express-app blog-api &amp;amp;&amp;amp; bit install --add-missing-deps

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

&lt;/div&gt;


&lt;p&gt;If you've done this correctly, you'll see the output:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7QcpnroW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.writergate.com/uploads/dc64a919-1200-42d3-90c6-9bc96dd9f4fa/xsyWTSY6f3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7QcpnroW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.writergate.com/uploads/dc64a919-1200-42d3-90c6-9bc96dd9f4fa/xsyWTSY6f3.png" alt="" width="707" height="105"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Figure: Creating the Express.js app&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Next, you'll see a directory as below:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--P4rxELs7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.writergate.com/uploads/dc64a919-1200-42d3-90c6-9bc96dd9f4fa/SkwYQpwtSC.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P4rxELs7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.writergate.com/uploads/dc64a919-1200-42d3-90c6-9bc96dd9f4fa/SkwYQpwtSC.png" alt="" width="462" height="354"&gt;&lt;/a&gt;Figure: Exploring the project directory&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Go ahead and open up your app root - &lt;code&gt;blog-api.app-root.ts&lt;/code&gt;. You'll see the following code:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sMAT3Tjs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.writergate.com/uploads/dc64a919-1200-42d3-90c6-9bc96dd9f4fa/28ziquqC4P.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sMAT3Tjs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.writergate.com/uploads/dc64a919-1200-42d3-90c6-9bc96dd9f4fa/28ziquqC4P.png" alt="" width="578" height="536"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Figure: The code generated in the app root&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The code above is enough to launch your app as a bare Express server. It initializes an app, registers routers, and then listens to a port that's resolved in the &lt;code&gt;getPort&lt;/code&gt; function. To start this Express App, let's run 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;bit use blog-api &amp;amp;&amp;amp; bit run blog-api

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

&lt;/div&gt;


&lt;p&gt;After you've done so, your Express.js app should spin up:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--D9FVaH74--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.writergate.com/uploads/dc64a919-1200-42d3-90c6-9bc96dd9f4fa/cVuS3yq6BM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--D9FVaH74--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.writergate.com/uploads/dc64a919-1200-42d3-90c6-9bc96dd9f4fa/cVuS3yq6BM.png" alt="" width="800" height="128"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Figure: Spinning up the Express App&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Next, you can open the sample endpoint - &lt;code&gt;localhost:3000/&lt;/code&gt;. This should launch your Express app and show the output:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_yregT9g--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.writergate.com/uploads/dc64a919-1200-42d3-90c6-9bc96dd9f4fa/x1J2FHQM1q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_yregT9g--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.writergate.com/uploads/dc64a919-1200-42d3-90c6-9bc96dd9f4fa/x1J2FHQM1q.png" alt="" width="728" height="220"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Figure: The sample app&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Okay, but, this doesn't truly speak about component-driven development. To see this in action, run &lt;code&gt;bit start&lt;/code&gt;. You should see a development server spin up:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UCkiVCmh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.writergate.com/uploads/dc64a919-1200-42d3-90c6-9bc96dd9f4fa/DeYbGcJNCP.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UCkiVCmh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.writergate.com/uploads/dc64a919-1200-42d3-90c6-9bc96dd9f4fa/DeYbGcJNCP.png" alt="" width="800" height="603"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Figure: Viewing the Workspace&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can click on the &lt;code&gt;blog-api&lt;/code&gt; and view it as its component:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4qbNcnsq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.writergate.com/uploads/dc64a919-1200-42d3-90c6-9bc96dd9f4fa/ihb3zOpV1K.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4qbNcnsq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.writergate.com/uploads/dc64a919-1200-42d3-90c6-9bc96dd9f4fa/ihb3zOpV1K.png" alt="" width="800" height="385"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Figure: Viewing the Express App component&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 03: Adding Routes to the API
&lt;/h2&gt;

&lt;p&gt;Next, let's add routes to our API. Let's do this by creating two new components:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; GetBlogs&lt;/li&gt;
&lt;li&gt; GetBlogById&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;But first, it's important to note that both of our Routes will use the same Entity - "Blog", and will have a single data source. So, let's first set up a Blog entity and a mock data set.&lt;/p&gt;

&lt;p&gt;To do so, let's create the entity component first using the command:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bit create node entities/blog

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

&lt;/div&gt;


&lt;p&gt;You should see the output shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FPJpOWPf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.writergate.com/uploads/dc64a919-1200-42d3-90c6-9bc96dd9f4fa/xMRmMS7HaU.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FPJpOWPf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.writergate.com/uploads/dc64a919-1200-42d3-90c6-9bc96dd9f4fa/xMRmMS7HaU.png" alt="" width="800" height="172"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Figure: Creating the Blog Entity Component&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Next, let's define the entity component by adding the following code to the &lt;code&gt;blog.ts&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * An entity class that represents a Blog
 */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Blog&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/**
   * the id of the blog post
   */&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;

  &lt;span class="cm"&gt;/**
   * the title of the blog
   */&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;_title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;

  &lt;span class="cm"&gt;/**
   * the plot of the blog (summarized version of the content)
   */&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;_plot&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;

  &lt;span class="cm"&gt;/**
   * the day the blog was published - in milliseconds
   */&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;_publishedDate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;publishedDate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_plot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_publishedDate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;publishedDate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;getId&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;getTitle&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_title&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;getPlot&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_plot&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;getPublishedDate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_publishedDate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;


&lt;p&gt;Now that we have an entity component, we can share this across all other components we create. This includes the sample data along with the other API Route components.&lt;/p&gt;

&lt;p&gt;So, next, let's create the Sample Data component using the command:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bit create node mocks/sample-data

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

&lt;/div&gt;


&lt;p&gt;Next, open the file &lt;code&gt;sample-data.ts&lt;/code&gt; and include the 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 { Blog } from '@dummyorg/blog.entities.blog';

const data: Blog[] = [
  new Blog("001", "Sample Title 01", "Sample Plot 01", Date.now()),
  new Blog("002", "Sample Title 02", "Sample Plot 02", Date.now()),
  new Blog("003", "Sample Title 03", "Sample Plot 03", Date.now()),
  new Blog("004", "Sample Title 04", "Sample Plot 04", Date.now()),
  new Blog("005", "Sample Title 05", "Sample Plot 05", Date.now()),
]

export function sampleData(): Blog[] {
  return data;
}

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

&lt;/div&gt;


&lt;p&gt;As you can see, we're using the entity component that we created before.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The cool thing about this is that it will automatically check and update all of its usage whenever we change the entity component.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, if we introduce a new field in the constructor for the Blog, the &lt;code&gt;sample-data&lt;/code&gt; will fail its build in the Ripple CI as the component cannot handle the new change introduced into the Blog entity.&lt;/p&gt;

&lt;p&gt;Next, if we head back to the development server and open the &lt;code&gt;sample-data&lt;/code&gt; component, you should be able to see the component in action:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qtu86ZjA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.writergate.com/uploads/dc64a919-1200-42d3-90c6-9bc96dd9f4fa/jRbP1U5mZD.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qtu86ZjA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.writergate.com/uploads/dc64a919-1200-42d3-90c6-9bc96dd9f4fa/jRbP1U5mZD.png" alt="image" width="800" height="519"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Figure: The Sample Data Component&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Next, let's set up the two routes using the command:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bit create node routes/get-blogs routes/get-blog-by-id

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

&lt;/div&gt;


&lt;p&gt;This will create two Node components that will act as the two routes in our Express.js API:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--I4HihclT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.writergate.com/uploads/dc64a919-1200-42d3-90c6-9bc96dd9f4fa/GX4oomq4mH.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--I4HihclT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.writergate.com/uploads/dc64a919-1200-42d3-90c6-9bc96dd9f4fa/GX4oomq4mH.png" alt="" width="800" height="207"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Figure: Creating the Components&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Next, you can define the implementations in the components as shown below:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// get-blogs.ts&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;sampleData&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@dummyorg/blog.mocks.sample-data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;blogs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sampleData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getBlogs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;blogs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getBlogsRoute&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;get&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;route&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/blogs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;middlewares&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;res&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getBlogs&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;



&lt;span class="c1"&gt;// get-blog-by-id.ts&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;sampleData&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@dummyorg/blog.mocks.sample-data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;blogs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sampleData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getBlogById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;blogById&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;blogs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;blog&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;blog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getId&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;blogById&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Blog not found&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;blogById&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getBlogByIdRoute&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;get&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;route&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/blogs/:id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;middlewares&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;res&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getBlogById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;



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

&lt;/div&gt;


&lt;p&gt;As you can see, we've not only implemented the business logic, but we've also exported two new "route" functions. These route functions create a middleware for each route and execute the business logic. We will leverage these middleware route functions in our API server to hook the routes to the app.&lt;/p&gt;

&lt;p&gt;Next, head over to your development server to view the two components:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eBGDGbcg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.writergate.com/uploads/dc64a919-1200-42d3-90c6-9bc96dd9f4fa/lWnDQfpCcT.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eBGDGbcg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.writergate.com/uploads/dc64a919-1200-42d3-90c6-9bc96dd9f4fa/lWnDQfpCcT.png" alt="image" width="800" height="661"&gt;&lt;/a&gt; &lt;strong&gt;Figure: Get Blogs Component&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KwqDoiI---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.writergate.com/uploads/dc64a919-1200-42d3-90c6-9bc96dd9f4fa/ksJ41dNXhp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KwqDoiI---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.writergate.com/uploads/dc64a919-1200-42d3-90c6-9bc96dd9f4fa/ksJ41dNXhp.png" alt="image" width="800" height="612"&gt;&lt;/a&gt; &lt;strong&gt;Figure: Get Blog By ID Component&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 04: Integrating the Routes to the API
&lt;/h2&gt;

&lt;p&gt;Now that we've built our API routes let's hook the routes to the API. To do so, head back to your API Component and open the &lt;code&gt;api-root&lt;/code&gt; file, and update the &lt;code&gt;registerRoutes&lt;/code&gt; function with the code shown below:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getBlogByIdRoute&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@dummyorg/blog.routes.get-blog-by-id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getBlogsRoute&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@dummyorg/blog.routes.get-blogs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;registerRoutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mockRoute&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getMockRoute&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="c1"&gt;// introduce new routes&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;routes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;mockRoute&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;getBlogByIdRoute&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nf"&gt;getBlogsRoute&lt;/span&gt;&lt;span class="p"&gt;()];&lt;/span&gt;
  &lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;route&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;middlewares&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;


&lt;p&gt;Next, all you have to do is launch the app by running the command:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bit run blog-api

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

&lt;/div&gt;


&lt;p&gt;Next, hit each endpoint to see the response:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Route - localhost:3000/blogs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fpmBgKkx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.writergate.com/uploads/dc64a919-1200-42d3-90c6-9bc96dd9f4fa/ZbyJ9ky3Ug.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fpmBgKkx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.writergate.com/uploads/dc64a919-1200-42d3-90c6-9bc96dd9f4fa/ZbyJ9ky3Ug.png" alt="image" width="800" height="187"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Route - localhost:3000/blogs/001&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LlBymFMy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.writergate.com/uploads/dc64a919-1200-42d3-90c6-9bc96dd9f4fa/GkXFLy9seY.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LlBymFMy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.writergate.com/uploads/dc64a919-1200-42d3-90c6-9bc96dd9f4fa/GkXFLy9seY.png" alt="image" width="800" height="90"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And, there we have it! Our API is up and running, and it didn't even take 5 minutes!&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 05: Sharing the API
&lt;/h2&gt;

&lt;p&gt;Next, let's share the API for everyone to view and contribute using the command:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bit tag &amp;amp;&amp;amp; bit export

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

&lt;/div&gt;


&lt;p&gt;This will automatically ship the components to the scope we created earlier and will kick off a Ripple Build.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yljemTva--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.writergate.com/uploads/dc64a919-1200-42d3-90c6-9bc96dd9f4fa/HocFGoqZfL.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yljemTva--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.writergate.com/uploads/dc64a919-1200-42d3-90c6-9bc96dd9f4fa/HocFGoqZfL.png" alt="" width="800" height="942"&gt;&lt;/a&gt;Figure: &lt;a href="https://bit.cloud/ripple-ci/job/dummyorg-main-bump-dependencies-versions-Q4Mrcks8o"&gt;The Ripple Build&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, Ripple CI knows the entire component tree and how each component is used.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 06: Automated Builds with Ripple CI
&lt;/h2&gt;

&lt;p&gt;Ripple CI not only builds your components but also tracks differences in the tree in which your component is being used and builds the tree to support the changes.&lt;/p&gt;

&lt;p&gt;For example, let's update the &lt;code&gt;get-blogs.ts&lt;/code&gt; route to return the following:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getBlogsRoute&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;get&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;route&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/blogs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;middlewares&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;res&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getBlogs&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="c1"&gt;// Return JSON now.&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;


&lt;p&gt;We've update the &lt;code&gt;res.json&lt;/code&gt; line to return a JSON object of &lt;code&gt;results&lt;/code&gt;. Let's sync this change to the cloud using the command:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bit tag &amp;amp;&amp;amp; bit export

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

&lt;/div&gt;


&lt;p&gt;Next, head over to Ripple CI to see the build:&lt;/p&gt;
&lt;h1&gt;
  
  
  &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AMinBczS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.writergate.com/uploads/dc64a919-1200-42d3-90c6-9bc96dd9f4fa/UH49qviFYn.png" alt="" width="454" height="550"&gt;
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Figure:&lt;/strong&gt; &lt;a href="https://bit.cloud/dummyorg/blog/~ripple-ci/job/dummyorg-main-bump-dependencies-versions-iPMYiMVPE"&gt;&lt;strong&gt;Ripple Build for Diff&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, Ripple identified that &lt;code&gt;get-blogs&lt;/code&gt; had changed, and as a result, it built the application as well. This ensures that our app is always up to date.&lt;/p&gt;

&lt;p&gt;Furthermore, you can extend the deployment to ship your app off to servers for hosting automatically!&lt;/p&gt;
&lt;h1&gt;
  
  
  Wrapping up
&lt;/h1&gt;

&lt;p&gt;Well, that was interesting. It's not every day that we build Express apps in under 5 minutes!&lt;/p&gt;

&lt;p&gt;If you like what you see, check out the full implementation, which includes unit-testing and component composition development - &lt;a href="https://bit.cloud/dummyorg/blog"&gt;all in my Bit Cloud Scope.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I hope you found this article helpful.&lt;/p&gt;

&lt;p&gt;Thank you for reading.&lt;/p&gt;
&lt;h1&gt;
  
  
  Learn More
&lt;/h1&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://blog.bitsrc.io/update-multiple-npm-packages-a1d49636ffca?source=collection_home---4------1-----------------------" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://res.cloudinary.com/practicaldev/image/fetch/s--5BdNVO9_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://miro.medium.com/v2/resize:fit:1200/1%2AQo9Hvgl_GPgy5yVOGxN0iQ.png" height="480" class="m-0" width="800"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://blog.bitsrc.io/update-multiple-npm-packages-a1d49636ffca?source=collection_home---4------1-----------------------" rel="noopener noreferrer" class="c-link"&gt;
          How To Update Multiple NPM Packages Effectively? | Lakindu Hewawasam | Bits and Pieces | Bits and Pieces
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;p class="truncate-at-3"&gt;
          Use Bit to ensure that your NPM packages are always up-to-date
        &lt;/p&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://res.cloudinary.com/practicaldev/image/fetch/s--OoWEHK9U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://miro.medium.com/v2/resize:fill:256:256/1%2A72WvVp4NqCU_4bfE6wCBEQ.png" width="128" height="128"&gt;
        blog.bitsrc.io
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



</description>
      <category>programming</category>
      <category>node</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Building a PDF Generator using AWS Lambda</title>
      <dc:creator>Lakindu Hewawasam</dc:creator>
      <pubDate>Sat, 08 Oct 2022 09:05:21 +0000</pubDate>
      <link>https://dev.to/aws-builders/building-a-pdf-generator-using-aws-lambda-4220</link>
      <guid>https://dev.to/aws-builders/building-a-pdf-generator-using-aws-lambda-4220</guid>
      <description>&lt;h2&gt;
  
  
  Using AWS Lambda, Amazon SQS, Amazon API Gateway, Amazon S3, AWS Lambda Layers and Pulumi to Build a Scalable PDF Generation Service
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.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%2F3wf7o31smf45l57a56az.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F3wf7o31smf45l57a56az.png" width="800" height="450"&gt;&lt;/a&gt;Almost all modern web applications use PDFs. For example, an application may utilize PDFs to send an email of an invoice for payment confirmation, a report or even a custom-typed document. For example, SignRequest uses PDFs to email a copy of a signed PDF document when all participants sign it. Likewise, there will come a time when you need to implement a PDF generator on your application as well. &lt;/p&gt;

&lt;p&gt;Therefore, as a serverless developer, it’s important to ensure that you can design scalable and resilient PDF generation solutions for applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The Easy Way Out: Front-End Generation&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;When tasked with PDF generation, a go-to option would be to generate the PDF on the client side with the available data. For example, if you’re using React, you might have used &lt;a href="https://www.npmjs.com/package/@react-pdf/renderer" rel="noopener noreferrer"&gt;@react-pdf/renderer&lt;/a&gt; to compile a PDF and download it directly in the web browser. Though it creates a fast and efficient process, it does have its set of negatives. For example: &lt;/p&gt;

&lt;p&gt;There is no authenticity: Since the PDF is being generated on the client side, the software product has no say in what gets compiled in the PDF. A user can easily tamper with the HTML content beforehand and compile the PDF in the client application before sending it to the backend. &lt;/p&gt;

&lt;p&gt;There is no traceability: You aren’t able to keep track of the location and the device that was used to create the PDF.&lt;/p&gt;

&lt;p&gt;There is no security: You cannot sign a document on the client side without exposing your private key. However, if you choose to sign your document on the client side, you will be putting your keys at risk of exposure. &lt;/p&gt;

&lt;p&gt;It does not scale: If your user is using a low-end device (or even Internet Explorer), your libraries may not be able to generate the PDF efficiently.&lt;/p&gt;

&lt;p&gt;Designing PDFs using the client-side approach is suitable for large-scale, demanding applications due to the discussed negatives.&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;strong&gt;The Best Way Out - Server-Side Generation&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;The recommended approach for PDF generation is to use the server side. This ensures that a client can obtain a copy of the PDF in a highly secure manner.&lt;/p&gt;

&lt;p&gt;Additionally, serverless computing helps you design and develop solutions that are highly available, resilient and cost-efficient.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Resilient, Scalable PDF Generation Architecture for Serverless
&lt;/h2&gt;

&lt;p&gt;Therefore, let us take a look at the serverless architecture for the solution that we will be building for the PDF service.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fxd0bc18f7412gcpo836g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fxd0bc18f7412gcpo836g.png" width="800" height="409"&gt;&lt;/a&gt;The architectural diagram shown above utilizes the AWS Serverless ecosystem to build a PDF generation service. It has:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; An API Gateway - For clients to request a PDF.&lt;/li&gt;
&lt;li&gt; An SQS Queue - The API Gateway will be integrated with a Lambda function that pushes data to an SQS Queue. This helps decouple the AWS Lambda function that generates the PDF, allowing the function to independently scale and not run out of possible concurrency limits. It allows the implementation of the &lt;a href="https://learn.microsoft.com/en-us/azure/architecture/patterns/queue-based-load-leveling" rel="noopener noreferrer"&gt;Queue-Based Load Levelling Design Pattern.&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt; An S3 Storage Bucket - To store the generated PDF&lt;/li&gt;
&lt;li&gt; SES - To email the signed URL to the client.&lt;/li&gt;
&lt;li&gt; DLQ - A Dead Letter Queue to handle error messages. The SQS Queue will push the failed messages to a DLQ if the Lambda processing a message fails a defined number of times.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Implementing the PDF Generation Service
&lt;/h2&gt;

&lt;p&gt;There are several ways this infrastructure can be provisioned. It could be:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Using the SDK&lt;/li&gt;
&lt;li&gt; Using the GUI&lt;/li&gt;
&lt;li&gt; Using Infrastructure as Code&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We will be using the IaC approach with Pulumi. Pulumi is an IaC tool that enables developers to provision infrastructure using their favourable programming language. It directly deploys infrastructure using the typed language, rather than transpiling back to CloudFormation, which makes your deployments a bit faster.&lt;/p&gt;

&lt;p&gt;Therefore, this implementation will be using Pulumi.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a Pulumi Project
&lt;/h2&gt;

&lt;p&gt;Initially, a new Pulumi project will be initialized using the &lt;a href="https://www.pulumi.com/docs/get-started/aws/begin/" rel="noopener noreferrer"&gt;Pulumi CLI&lt;/a&gt;. Ensure you've installed the CLI and have configured an IAM User with an Access Key in your AWS Profile.&lt;/p&gt;

&lt;p&gt;Run the command shown below to create a new Pulumi project with TypeScript in a default &lt;code&gt;dev&lt;/code&gt; stacks in your preferred AWS Region (mine is US East 01).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haxe"&gt;&lt;code&gt;&lt;span class="nx"&gt;pulumi&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;typescript&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.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%2Fnl1gl5r2tqge30cnuga9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fnl1gl5r2tqge30cnuga9.png" width="568" height="309"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Figure - Creating a new Pulumi Project&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Afterwards, you should see the output shown below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F1qs9req170rrxunoih75.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F1qs9req170rrxunoih75.png" width="564" height="65"&gt;&lt;/a&gt;&lt;strong&gt;Figure - Creating the Pulumi Project successfully&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Hereafter, we can start provisioning the required infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Defining the Infrastructure
&lt;/h2&gt;

&lt;p&gt;The source code will be structured into directories where each directory holds a specific service. Hence, the project will have four directories:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; ses&lt;/li&gt;
&lt;li&gt; sqs&lt;/li&gt;
&lt;li&gt; lambda&lt;/li&gt;
&lt;li&gt; api&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Provisoning the SQS Queues&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Firstly, the two SQS queues will be provisioned. One queue will act as a DLQ while the other queue will integrate with the API Gateway.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@pulumi/aws&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;deadLetterQueue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sqs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Queue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;deadLetterQueue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;deadLetterQueue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;visibilityTimeoutSeconds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 2 minutes visibility timeout&lt;/span&gt;

  &lt;span class="na"&gt;messageRetentionSeconds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1209600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// remove messages in DLQ after 14 days&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pdfProcessingQueue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sqs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Queue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pdfProcessingQueue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pdfProcessingQueue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;// 1 minute visibility timeout for processing// within 1 minute -&amp;gt; create PDF, upload to S3, send a message, delete message from queue// else it will be visible again and can cause double processing, &lt;/span&gt;

  &lt;span class="na"&gt;visibilityTimeoutSeconds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="err"&gt;﻿&lt;/span&gt;
  &lt;span class="na"&gt;messageRetentionSeconds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;86400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// remove messages in queue after 24 hours&lt;/span&gt;

  &lt;span class="na"&gt;fifoQueue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

  &lt;span class="na"&gt;delaySeconds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// delay message for 10 seconds to avoid possible lambda limits.&lt;/span&gt;

  &lt;span class="c1"&gt;// redrive policy to push to DLQ after message fails three times&lt;/span&gt;
&lt;span class="err"&gt;﻿&lt;/span&gt;
  &lt;span class="na"&gt;redrivePolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;deadLetterQueue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;deadLetterTargetArn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;arn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;maxReceiveCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}))&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

 &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Queues&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;deadLetterQueue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;pdfProcessingQueue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;The snippet shown above highlights the two SQS Queues set up. I've made certain design decisions when designing the queues&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; All messages in DLQ will be removed after 14 days&lt;/li&gt;
&lt;li&gt; All messages in PDF Processing Queue will be removed after 1 day, while each message will have a visibility timeout of 1 minute (assuming that the entire process will occur in under a minute)&lt;/li&gt;
&lt;li&gt; Additionally, each message has a delay message for 10 seconds to allow the Lambda function to process each Queue record without exceeding possible limits.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Adding a Lambda Function to Poll The PDF Processing Queue&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once the PDF Processing Queue recieves a message, it needs to be processed. To do so, a Lambda function must poll the queue to fetch records to process. Setting up a Lambda function that polls the SQS queue is as simple as its shown below (since Pulumi takes care of the underlying IAM Policies).&lt;/p&gt;

&lt;p&gt;However, since this Lambda function generates a PDF of a file, it needs two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; A Chromium Engine: Needed to create an HTML Page, paint the PDF&lt;/li&gt;
&lt;li&gt; SDK to control the chromium browser: Needed to control the rendering engine to create the HTML page, paint the PDF and to fetch the compiled PDF stream.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A browser rendering engine such as chromium is quite large in size. In fact, there is &lt;a href="https://www.npmjs.com/package/chrome-aws-lambda" rel="noopener noreferrer"&gt;one library available&lt;/a&gt; for AWS in specific called &lt;code&gt;chrome-aws-lambda&lt;/code&gt; that provides a Chromium Rendering Engine + Puppeteer to control the browser. This allows users to seamlessly generate PDFs inside a Lambda function.&lt;/p&gt;

&lt;p&gt;However, this library is around 65MB which is typically quite heavy for a single Lambda function. Therefore, we can utilize a Lambda Layer to package this library as a seperate resource and ultimately use it in any Lambda function that we need, by simply including the layer.&lt;/p&gt;

&lt;p&gt;Therefore, first download the library and package it using the command below. This will package it in the format needed for the Lambda Layer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone --depth=1 https://github.com/alixaxel/chrome-aws-lambda.git &amp;amp;&amp;amp; \
cd chrome-aws-lambda &amp;amp;&amp;amp; \
make chrome_aws_lambda.zip

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

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;chrome_aws_lambda.zip&lt;/code&gt; will be the file that we will be uploading to our layer. It creates a folder structure like &lt;code&gt;nodejs/node_modules&lt;/code&gt; that is the recommended directory structure to allow Lambda functions to let users automatically import the library at execution time.&lt;/p&gt;

&lt;p&gt;Hereafter, provision the lambda layer as below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const pdfLayer = new aws.lambda.LayerVersion("pdfLayer", {
    layerName: "pdfLayer",
    code: new pulumi.asset.AssetArchive({
        ".": new pulumi.asset.FileArchive("./lambda/layer/chrome_aws_lambda.zip"),
    }),
    compatibleRuntimes: [aws.lambda.Runtime.NodeJS16dX],
});

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

&lt;/div&gt;



&lt;p&gt;Hereafter, we can create a Lambda Function that uses the defined layer and integrate it with SQS as shown below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// in lambda directory
﻿export const pdfProcessingLambda = new aws.lambda.CallbackFunction("pdfProcessingLambda", {
    callback: async (event: aws.sqs.QueueEvent) =&amp;gt; { },
    memorySize: 3072, // use 3gb memory
    runtime: aws.lambda.Runtime.NodeJS16dX,
    timeout: 30,
    layers: [pdfLayer.arn] // bind layer
});

// in sqs directory
pdfProcessingQueue.onEvent("pdfProcessingQueueEvent", pdfProcessingLambda);

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Adding the PDF Generator Logic&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the snippet shown above, we've provisioned a Lambda function named "pdfProcessingLambda". We can add its implementation that helps compiled the pdf. This is shown below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;generatePdf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chromium&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;chrome-aws-lambda&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// launch a headless chrome instanceconst executablePath = await chromium.executablePath;&lt;/span&gt;
    &lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;puppeteer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;executablePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;headless&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headless&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;defaultViewport&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaultViewport&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// create a new page&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;h1&amp;gt; Hi! Here is a copy of your PDF Content that you requested!&amp;lt;/h1&amp;gt; &amp;lt;br/&amp;gt; &amp;lt;hr/&amp;gt; &amp;lt;p&amp;gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &amp;lt;/p&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
&lt;span class="c1"&gt;// set the content of the page&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="err"&gt;﻿&lt;/span&gt;

    &lt;span class="c1"&gt;// generate the pdf as a buffer and return it&lt;/span&gt;

&lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;A4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}))&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="err"&gt;﻿&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// close the browser&lt;/span&gt;
&lt;span class="err"&gt;﻿&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;A function (shown above) will accept content passed from the client and utilize chromium to generate the PDF via puppeteer. It initially launches an instance of chrome, sets the page content and returns a Buffer as an A4 (which holds the PDF).&lt;/p&gt;

&lt;p&gt;The Lambda function code will be updated to utilize this function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const pdfProcessingLambda = new aws.lambda.CallbackFunction("pdfProcessingLambda", {
  callback: async (event: aws.sqs.QueueEvent) =&amp;gt; {
    const processedEventPromises = event.Records.map(async (record) =&amp;gt; {
      const { messageId, body, receiptHandle } = record;
      const { content, email } = JSON.parse(body) as {
        email: string;
        content: string;
      };

      // generate pdfconst pdf = await generatePdf(content);

      const pdfName = `${messageId}.pdf`;

      // upload pdf to s3

      const s3 = new aws.sdk.S3({ region: "eu-central-1" });
      await s3.putObject({
        Bucket: pdfBucket.bucket.get(),
        Key: `pdf/${pdfName}`,
        Body: pdf,
        ContentType: "application/pdf",
      }).promise();

      // generate signed url from s3 for public reads.

      const signedUrl = await s3.getSignedUrlPromise("getObject", {
        Bucket: pdfBucket.bucket.get(),
        Key: `pdf/${pdfName}`,
        Expires: 60 * 60 * 24 * 7, // 7 days
      });

      // send email with signed url

     const ses = new aws.sdk.SES({ region: "us-east-1" });

      await ses.sendEmail({
        Source: senderEmail,
        Destination: { ToAddresses: [email], },
        Message: {
          Body: { Html: { Charset: "UTF-8", Data: `Your pdf is ready. &amp;lt;a href="${signedUrl}"&amp;gt;Download&amp;lt;/a&amp;gt;`, }, },
          Subject: { Data: "Your pdf is ready", Charset: "UTF-8" },
        },
      }).promise();

      // delete message from queue

      await sqs.deleteMessage({ QueueUrl: Queues.pdfProcessingQueue.url.get(), ReceiptHandle: receiptHandle }).promise();
      console.log(`Deleted message ${messageId} from queue`);
    });
    await Promise.all(processedEventPromises);
  },
  memorySize: 3072,
  runtime: aws.lambda.Runtime.NodeJS14dX,
  timeout: 30,
  layers: [pdfLayer.arn],
  policies: [ManagedPolicies.AmazonSESFullAccess, ManagedPolicies.AmazonS3FullAccess, ManagedPolicies.AmazonSQSFullAccess, ManagedPolicies.AWSLambdaBasicExecutionRole, ManagedPolicies.CloudWatchFullAccess],
});

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

&lt;/div&gt;



&lt;p&gt;The PDF generated by the function will then be used in the Lambda function shown above and will upload it to S3 and return a signed URL for the client via an email so that the client can read the PDF.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE: Ensure that the identities that you are using via SES have been created as verified identifies.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The snippet shown above utilizes an S3 bucket. This bucket will be used to store the PDFs and must be restricted from zublic access and can only be accessed via signed URL. Its implementation with the required resource based policies is shown below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight viml"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; * &lt;span class="k"&gt;as&lt;/span&gt; aws from &lt;span class="s2"&gt;"@pulumi/aws"&lt;/span&gt;;

&lt;span class="k"&gt;const&lt;/span&gt; provider &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; aws&lt;span class="p"&gt;.&lt;/span&gt;Provider&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"provider"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; region&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'eu-central-1'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; pdfBucket &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; aws&lt;span class="p"&gt;.&lt;/span&gt;s3&lt;span class="p"&gt;.&lt;/span&gt;Bucket&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"pdf-bucket-lak"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    bucket&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"pdf-bucket-lak"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    acl&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'private'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    policy&lt;span class="p"&gt;:&lt;/span&gt; JSON&lt;span class="p"&gt;.&lt;/span&gt;stringify&lt;span class="p"&gt;({&lt;/span&gt;
        Version&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        Statement&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                Sid&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'AllowPutObjectForLambda'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                Effect&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'Allow'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                Principal&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    Service&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'lambda.amazonaws.com'&lt;/span&gt;
                &lt;span class="p"&gt;},&lt;/span&gt;
                Action&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'s3:PutObject'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                Resource&lt;span class="p"&gt;:&lt;/span&gt; `arn&lt;span class="p"&gt;:&lt;/span&gt;aws&lt;span class="p"&gt;:&lt;/span&gt;s3&lt;span class="p"&gt;:::&lt;/span&gt;pdf&lt;span class="p"&gt;-&lt;/span&gt;bucket&lt;span class="p"&gt;-&lt;/span&gt;lak&lt;span class="sr"&gt;/pdf/&lt;/span&gt;*`
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                Sid&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'AllowGetObjectForLambda'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                Effect&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'Allow'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                Principal&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    Service&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'lambda.amazonaws.com'&lt;/span&gt;
                &lt;span class="p"&gt;},&lt;/span&gt;
                Action&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'s3:GetObject'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                Resource&lt;span class="p"&gt;:&lt;/span&gt; `arn&lt;span class="p"&gt;:&lt;/span&gt;aws&lt;span class="p"&gt;:&lt;/span&gt;s3&lt;span class="p"&gt;:::&lt;/span&gt;pdf&lt;span class="p"&gt;-&lt;/span&gt;bucket&lt;span class="p"&gt;-&lt;/span&gt;lak&lt;span class="sr"&gt;/pdf/&lt;/span&gt;*`
            &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; provider &lt;span class="p"&gt;})&lt;/span&gt;;

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

&lt;/div&gt;



&lt;p&gt;The bucket is being provisioned on AWS Global Infrastructure and has the resource based policies to only allow writes from a Lambda function, and denying all public reads.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Provisioning the API Gateway&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;An API Gateway which invokes a Lambda function that pushes data to an SQS Queue will be provisioned.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;awsx&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@pulumi/awsx&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@pulumi/aws&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Queues&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../sqs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sqs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SQS&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;us-east-1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiGateway&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;awsx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;apigateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;API&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;api&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/pdf&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;eventHandler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// client passes email and content to add to pdf&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;{}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="err"&gt;﻿&lt;/span&gt;

        &lt;span class="c1"&gt;// construct message to send to SQS&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sqsParams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;MessageBody&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;&lt;span class="na"&gt;QueueUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="nx"&gt;Queues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pdfProcessingQueue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="err"&gt;﻿&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="c1"&gt;// send message to SQS&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sqs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sqsParams&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;promise&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="err"&gt;﻿&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MessageId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// return message id to client for tracking purposes&lt;/span&gt;

       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;MessageId&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;&lt;span class="err"&gt;﻿&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;restApiArgs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;binaryMediaTypes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;The snippet shown above highlights an API Gateway being provisioned with one endpoint &lt;code&gt;/pdf&lt;/code&gt; that has a Lambda function that obtains the email and content sent by client and pushes it to the queue. Finally, it returns the message ID that the client can use to track the status of the message.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying
&lt;/h2&gt;

&lt;p&gt;Navigate to the &lt;code&gt;index.ts&lt;/code&gt; file and import all added infrastructure so the Pulumi Deployment Service can deploy it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Queues&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./sqs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;apiGateway&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./api&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dlqUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Queues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deadLetterQueue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pdfProcessingQueueUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Queues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pdfProcessingQueue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiGatewayUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;apiGateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Finally, you can deploy the infrastructure using the command &lt;code&gt;pulumi up --stack dev -skip-preview --yes&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Upon successful deployment, the output shown below will be illustrated.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F3g6842qk30ql6ocvhb6z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F3g6842qk30ql6ocvhb6z.png" width="800" height="723"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Figure - Deploying the infrastructure&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing the Application
&lt;/h2&gt;

&lt;p&gt;Hereafter, you can use the &lt;code&gt;apiGatewayUrl&lt;/code&gt; to invoke the API as shown below and observe the architecture in action, and generating a PDF which will be emailed to you.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fugs1j1qsrbqpc9yd9kei.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fugs1j1qsrbqpc9yd9kei.png" width="639" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Figure: Executing the API Call&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fmdzg00egm4mvdx5ngur0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fmdzg00egm4mvdx5ngur0.png" width="800" height="216"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Figure: Receiving the email&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F6aj76p61q56hzyp2efqd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F6aj76p61q56hzyp2efqd.png" width="800" height="490"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Figure: Viewing the generated PDFs&lt;/strong&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Wrapping Up
&lt;/h1&gt;

&lt;p&gt;And there we have it! We've successfully designed and implemented a secure, robust, resilient and scalable PDF generation service using AWS Serverless Computing!&lt;/p&gt;

&lt;p&gt;The code implemented in this article is accessible in the &lt;a href="https://github.com/lakindu2002/pulumi-aws-pdf-generator" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I hope you that have found this article helpful!&lt;/p&gt;

&lt;p&gt;Thank you for reading!&lt;/p&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>programming</category>
      <category>pdfgen</category>
    </item>
  </channel>
</rss>
