<?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: Jordi</title>
    <description>The latest articles on DEV Community by Jordi (@ledfusion).</description>
    <link>https://dev.to/ledfusion</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%2F85893%2F1b5aa44b-f22c-4332-851b-30870b5e62e8.jpg</url>
      <title>DEV Community: Jordi</title>
      <link>https://dev.to/ledfusion</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ledfusion"/>
    <language>en</language>
    <item>
      <title>If TDD is Zen, adding Serverless brings Nirvana - part #2</title>
      <dc:creator>Jordi</dc:creator>
      <pubDate>Mon, 23 Jul 2018 15:54:22 +0000</pubDate>
      <link>https://dev.to/ledfusion/if-tdd-is-zen-adding-serverless-brings-nirvana---part-2-f0</link>
      <guid>https://dev.to/ledfusion/if-tdd-is-zen-adding-serverless-brings-nirvana---part-2-f0</guid>
      <description>

&lt;h1&gt;If TDD is Zen, adding Serverless brings Nirvana (part #2)&lt;/h1&gt;

&lt;p&gt;If you are one of the happy readers of &lt;a href="https://dev.to/ledfusion/if-tdd-is-zen-adding-serverless-brings-nirvana-545c"&gt;my article&lt;/a&gt; about &lt;a href="https://en.wikipedia.org/wiki/Test-driven_development"&gt;TDD&lt;/a&gt; + &lt;a href="https://serverless.com/"&gt;Serverless&lt;/a&gt; architecture, just keep reading because today we are going to improve our serverless toolkit with mongoose, connection pooling and monitoring.&lt;/p&gt;

&lt;p&gt;If you haven’t yet, I strongly encourage you to read part #1 first and get back once you are happy, up and running.&lt;/p&gt;


&lt;div class="ltag__link"&gt;
        &lt;a href="/ledfusion" class="ltag__link__link"&gt;
          &lt;div class="ltag__link__pic"&gt;
            &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ml3tcLMw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s---wiO9tG8--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://thepracticaldev.s3.amazonaws.com/uploads/user/profile_image/85893/1b5aa44b-f22c-4332-851b-30870b5e62e8.jpg" alt="ledfusion image"&gt;
          &lt;/div&gt;&lt;/a&gt;
          &lt;a href="/ledfusion/if-tdd-is-zen-adding-serverless-brings-nirvana-545c" class="ltag__link__link"&gt;
            &lt;div class="ltag__link__content"&gt;
              &lt;h2&gt;If TDD is Zen, adding Serverless brings Nirvana&lt;/h2&gt;
              &lt;h3&gt;Jordi Moraleda&lt;/h3&gt;
              &lt;div class="ltag__link__taglist"&gt;
&lt;span class="ltag__link__tag"&gt;#serverless&lt;/span&gt;&lt;span class="ltag__link__tag"&gt;#aws&lt;/span&gt;&lt;span class="ltag__link__tag"&gt;#lambda&lt;/span&gt;&lt;span class="ltag__link__tag"&gt;#tdd&lt;/span&gt;
&lt;/div&gt;
            &lt;/div&gt;
        &lt;/a&gt;
      &lt;/div&gt;


&lt;p&gt;So if part #1 is OK for you but still need more, let’s see how we can make our DB perform fast and furious.&lt;/p&gt;

&lt;h2&gt;Database model&lt;/h2&gt;

&lt;p&gt;If we want our code to be easily maintainable, lambda functions and TDD are our friends. But when dealing with the database, we clearly can do better to document and enforce a database schema for our backend.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;mongodb&lt;/code&gt; NPM package is the lightest way to implement a call to our database. However, we may want to consider using an ODM like &lt;a href="http://mongoosejs.com/"&gt;Mongoose&lt;/a&gt;, as the benefits we get are worth sacrificing just a bit of performance. We will be tweaking performance in a few minutes.&lt;/p&gt;

&lt;p&gt;So, starting on the &lt;a href="https://github.com/ledfusion/serverless-tdd-starter/tree/part-1"&gt;code from episode #1&lt;/a&gt;, let’s install Mongoose and get started:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm uninstall mongodb
npm install mongoose
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Ready! Now we can define our first model.&lt;/p&gt;

&lt;p&gt;As a team member joining the project I would clearly go to the models folder if I checked out the code for the first time. So let’s create the folder and add a model for a user in &lt;code&gt;models/user.js&lt;/code&gt;.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;  &lt;/div&gt;

&lt;p&gt;As you may notice, we are defining the three fields we used in the starter project as &lt;code&gt;String&lt;/code&gt; and &lt;code&gt;required&lt;/code&gt; and we are also definig an extra field created which will be populated with the result of evaluating &lt;code&gt;Date.now&lt;/code&gt; upon creation.&lt;/p&gt;

&lt;p&gt;Now we can use our DB model object and invoke methods on it to perform operations on the database with type validation and schema compliance. We also get a more compact and cleaner code.&lt;/p&gt;

&lt;p&gt;For example, to find all users with mongodb we needed to do:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const dbUsers = dbClient.db(dbName).collection(dbCollection)
const result = await dbUsers.findOneAndUpdate({ _id: ObjectID(userId) }, { $set: newUser })
return result.value._id
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Whereas now, the same with mongoose would look like:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const User = require("./models/user.js")
const result = await User.findByIdAndUpdate(userId, newUser)
return result._id
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You see, shorter, quicker… and we get schema validation. But you may think: &lt;em&gt;What about the extra overhead?&lt;/em&gt; Right, let’s see what happens when we query an object created as &lt;code&gt;{ name: "John", lastName: "Smith", email: "john@example.com" }&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bRu8uQ2R--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2AmCSNqqSe5pNzHHYN2IJ7fg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bRu8uQ2R--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2AmCSNqqSe5pNzHHYN2IJ7fg.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Objects returned by Mongoose are not the plain JS objects we would expect. They behave like JS Proxy objects, with internal getters, setters, caches and more. This happens in order to detect whether a field was updated and whether the object needs to be synced or not. All of this magic comes at a cost.&lt;/p&gt;

&lt;p&gt;However, mongoose allows us to enable the lean mode, skip the extra housekeeping and deal with plain JS objects. Schema validation will only be done when updating or creating documents. Queries will just pass the data. How that?&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;await User.findByIdAndUpdate(userId, newUser).lean()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Easy, right? So now, let’s rework our handlers and see how they look:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;  &lt;/div&gt;

&lt;p&gt;Now, our beloved tests:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--01-y7xez--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2ACemacGmkE-eker2naRS1sA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--01-y7xez--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2ACemacGmkE-eker2naRS1sA.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;See? We rewrote most our logic, but in just 132ms we know that we broke nothing. That’s what I meant by TDD and zen.&lt;/p&gt;

&lt;h2&gt;Connection pooling&lt;/h2&gt;

&lt;p&gt;But we still have room for improvement. As you just saw, our handler connects and disconnects from the DB server at every request. This is due to the constraints of the serverless architecture, the process starts from scratch at every request and exists once the event loop is empty.&lt;/p&gt;

&lt;p&gt;However, there is a small serverless trick that can be used to keep our process frozen when a response is sent, instead of exiting. This would allow us to keep our context and reuse database connections that were already established.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“Sounds good, show me the money”&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Sure, in your handler, set the following property to &lt;code&gt;false&lt;/code&gt; at the begining of the function:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;exports.list = async (event, context) =&amp;gt; {
  context.callbackWaitsForEmptyEventLoop = false

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

&lt;p&gt;Since now our function may already have DB connections available, we need to encapsulate our connection logic and ensure connectivity across executions:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;  &lt;/div&gt;

&lt;p&gt;This is fine, but we need to do an extra step with our DB models. They need to be attached to the currently active mongoose connection, so the previous code will not work anymore.&lt;/p&gt;

&lt;p&gt;Let’s change &lt;code&gt;models/user.js&lt;/code&gt; and return the schema instead of registering it to the global mongoose context.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;  &lt;/div&gt;

&lt;p&gt;Then, we need a last step in our &lt;code&gt;ensureDbConnected&lt;/code&gt; function:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;  &lt;/div&gt;

&lt;p&gt;Now in our handlers we just need to replace the connection logic by a call to &lt;code&gt;ensureDbConnected&lt;/code&gt; and remove the calls to &lt;code&gt;disconnect&lt;/code&gt;. So our handlers now look like this:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;  &lt;/div&gt;

&lt;p&gt;Every new developer on your team will have little to no problem in mastering such code.&lt;/p&gt;

&lt;h2&gt;The gotcha&lt;/h2&gt;

&lt;p&gt;However, now there is a small issue.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OOKfmDon--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2AlKJnW-nvYysIYL86yWBDBA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OOKfmDon--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2AlKJnW-nvYysIYL86yWBDBA.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you nice, now the Node process does not just exit when tests are complete. We told serverless to freeze our context instead of letting the event loop be empty, so now the process will not exit because of that.&lt;/p&gt;

&lt;p&gt;This is okay if testing and deployment are performed by the project maintainer, but definitely becomes an issue if the testsuite is part of a pipeline that performs &lt;strong&gt;CI/CD tasks&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;There may well be better approaches, but the solution I’ve found to work in our projects is adding hooks to every test suite and force the process to exit when they are done. Create the file &lt;code&gt;test/test-completion-hooks.js&lt;/code&gt; like this:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;  &lt;/div&gt;

&lt;p&gt;We need to register them in every test suite (by now, just &lt;code&gt;test/user.spec.js&lt;/code&gt;). The result change is very simple:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;  &lt;/div&gt;

&lt;p&gt;There are two lines to include in every test suite, but usingaddCompletionHooks is the solution that I’ve found to work best. If you have a better approach to get around this limitation, I’ll be more than glad to read it from you on the comments :)&lt;/p&gt;

&lt;p&gt;So we have DB models, we keep connections open and our testing is working. What next?&lt;/p&gt;

&lt;h2&gt;Webpack&lt;/h2&gt;

&lt;p&gt;Let’s optimize the code we are shipping. It is no secret that &lt;code&gt;node_modules&lt;/code&gt; can easily contain hunderds of megabytes of data that will be never used. However, serverless will package the main folder of your project, except for development dependencies in &lt;code&gt;node_modules&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Even if we only wrote 455 lines of code at the moment, running &lt;code&gt;serverless package&lt;/code&gt; results in a 2.3Mb file, which expands into 11Mb of uncompressed data.&lt;/p&gt;

&lt;p&gt;We can surely do better by making use of webpack’s tree shaking capabilities. Let’s install the package:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -D serverless-webpack webpack
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;At the time of writing, webpack version 4 is supported.&lt;/p&gt;

&lt;p&gt;Now we need to declare the plugin in &lt;code&gt;serverless.yml&lt;/code&gt; &amp;gt; &lt;code&gt;plugins&lt;/code&gt; &lt;strong&gt;above&lt;/strong&gt; the declaration of serverless-offline :&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# ...

plugins:
  - serverless-webpack
  - serverless-offline
  - serverless-mocha-plugin
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Next, we need a config file for webpack, so we will create webpack.config.js :&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;  &lt;/div&gt;

&lt;p&gt;That’s all! Now we just need to npm run deploy again and now everything will be bundled into a single JS file.&lt;/p&gt;

&lt;h2&gt;Comparison&lt;/h2&gt;

&lt;p&gt;After all these changes, how well are we doing? Let’s use &lt;a href="https://httpd.apache.org/docs/2.4/programs/ab.html"&gt;ApacheBench&lt;/a&gt; to see if we made any difference.&lt;/p&gt;

&lt;p&gt;Before we made any change, this was the result of making 1000 requests to the &lt;code&gt;listUser&lt;/code&gt; function:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ab -k -c 50 -n 1000 [https://hkyod1qhuh.execute-api.eu-central-1.amazonaws.com/staging/users](https://hkyod1qhuh.execute-api.eu-central-1.amazonaws.com/staging/users)

This is ApacheBench, Version 2.3 &amp;lt;$Revision: 1807734 $&amp;gt;

[...]

Concurrency Level:      50
Time taken for tests:   39.649 seconds
Complete requests:      1000
Failed requests:        423
   (Connect: 0, Receive: 0, Length: 423, Exceptions: 0)
Non-2xx responses:      423
Keep-Alive requests:    1000
Total transferred:      493011 bytes
HTML transferred:       28317 bytes
Requests per second:    25.22 [#/sec] (mean)
Time per request:       1982.445 [ms] (mean)
Time per request:       39.649 [ms] (mean, across all concurrent requests)
Transfer rate:          12.14 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0   23  98.7      0     466
Processing:    83 1753 2052.4   1001    7927
Waiting:       83 1753 2052.4   1001    7927
Total:         83 1775 2065.3   1001    7927

Percentage of the requests served within a certain time (ms)
  50%   1001
  [...]
  99%   6996
 100%   7927 (longest request)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The benckmark is made upon a starter plan of MongoDB Atlas, which has a limit of 100 simultaneous connections. Even if we set a concurrency limit of 50, we see that 423 requests are still failing because they try to open a connection that surpasses this limit.&lt;/p&gt;

&lt;p&gt;Now let’s run the same with the improvements we made in this article and see how it goes:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ab -k -c 50 -n 1000 [https://hkyod1qhuh.execute-api.eu-central-1.amazonaws.com/staging/users](https://hkyod1qhuh.execute-api.eu-central-1.amazonaws.com/staging/users)

This is ApacheBench, Version 2.3 &amp;lt;$Revision: 1807734 $&amp;gt;

[...]

Concurrency Level:      50
Time taken for tests:   10.087 seconds
Complete requests:      1000
Failed requests:        0
Keep-Alive requests:    1000
Total transferred:      465000 bytes
HTML transferred:       2000 bytes
Requests per second:    99.14 [#/sec] (mean)
Time per request:       504.335 [ms] (mean)
Time per request:       10.087 [ms] (mean, across all concurrent requests)
Transfer rate:          45.02 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0   19  85.2      0     468
Processing:    61  484 406.7    370    1079
Waiting:       61  484 406.7    370    1079
Total:         61  503 399.9    487    1079

Percentage of the requests served within a certain time (ms)
  50%    487
  [...]
  99%   1010
 100%   1079 (longest request)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Simple and clear, the actual &lt;strong&gt;throughput is 4x&lt;/strong&gt;. And the number of failed requests (because of no DB connection) &lt;strong&gt;drops to zero&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; There is no remarkable performance improvement in adding webpack alone. Keep it if you like to store lighter files on S3, but you can also disable the plugin and achieve faster response/rebuild times while developing.&lt;/p&gt;

&lt;p&gt;Needless to say: before jumping into production, do your own distributed benckmarks with a scalable DB hosting plan and setting a and much higher number of connections.&lt;/p&gt;

&lt;h2&gt;Monitoring&lt;/h2&gt;

&lt;p&gt;Monitoring lambda functions is a subject that could well cover another article by itself. However, there are some tools that we can take advantage of, out of the box.&lt;/p&gt;

&lt;p&gt;Let’s have a look at the lambda we have been benchmarking: &lt;code&gt;listUsers&lt;/code&gt;. Open &lt;a href="https://eu-central-1.console.aws.amazon.com/lambda/home"&gt;AWS Lambda&lt;/a&gt;, click the function on the list and select the “Monitoring” tab.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--J0p-ZIPc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2750/1%2AxeeJ3a8GmChGxjtX337Kew.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--J0p-ZIPc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2750/1%2AxeeJ3a8GmChGxjtX337Kew.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You’ll be presented with a few charts giving you an overview of the function’s status. To check the status of the DB cluster, we can also refer to &lt;a href="https://cloud.mongodb.com/v2#/clusters"&gt;MongoDB Atlas&lt;/a&gt;, open our cluster and open the metrics section.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VL6nMTSh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2590/1%2AzE1g1a8YsV65lA-zjY1y5w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VL6nMTSh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2590/1%2AzE1g1a8YsV65lA-zjY1y5w.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we got into a scenario in which we would like to diagnose a problem, we can get back to the Lambda function console and click on the “View logs in CloudWatch” button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8a0TuSRF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2AQkl3QN0y42qlipe9AhN1eA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8a0TuSRF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2AQkl3QN0y42qlipe9AhN1eA.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s click on the latest log stream:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gk-8vR4B--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2570/1%2AWjxWtruzbZd7TvhXmENXUQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gk-8vR4B--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2570/1%2AWjxWtruzbZd7TvhXmENXUQ.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we can explore execution times, billed times, memory usage, etc.&lt;/p&gt;

&lt;p&gt;If you need to investigate the internals of a function’s execution, you can make use of &lt;code&gt;console.log&lt;/code&gt;, &lt;code&gt;console.err&lt;/code&gt;, etc. Let’s add some logging on our lambda function:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;console.log("&amp;gt; I AM AN EVENT PRINTED BY console.log()")

console.error("&amp;gt; AND I AM AN ERROR REPORTED BY console.error()", new Error("Error message"))
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If we deploy our code and open the latest log stream entry, we will be able to see them:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CpPViBLm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2572/1%2AQmJsHukelMWq1H0qx-60aw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CpPViBLm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2572/1%2AQmJsHukelMWq1H0qx-60aw.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is room to go deeper in this subject. Depending on your needs, you can have a look at &lt;a href="https://github.com/alex-murashkin/serverless-plugin-tracing"&gt;serverless-plugin-tracing&lt;/a&gt; , along with &lt;a href="https://eu-central-1.console.aws.amazon.com/xray/home"&gt;AWS X-Ray&lt;/a&gt;, or check &lt;a href="https://dashbird.io/"&gt;Dashbird&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;Wrap up&lt;/h2&gt;

&lt;p&gt;If you want to experiment with the code of the article, I have updated the repo in a dedicated branch: &lt;a href="https://github.com/ledfusion/serverless-tdd-starter/tree/part-2"&gt;https://github.com/ledfusion/serverless-tdd-starter/tree/part-2&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This concludes part #2 of my article on how to achieve nirvana by using TDD, along with Serverless. I hope that you found it useful, and don’t forget to clap, comment, share and smile if you want more and more :)&lt;/p&gt;

&lt;p&gt;BTW: I am available to help you grow your projects. &lt;br&gt;
Feel free to find me on &lt;a href="https://jordi-moraleda.github.io/"&gt;https://jordi-moraleda.github.io/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks for your read.&lt;/p&gt;




&lt;p&gt;Cover photo by Marion Michele on Unsplash&lt;/p&gt;


</description>
      <category>serverless</category>
      <category>aws</category>
      <category>lambda</category>
      <category>tdd</category>
    </item>
    <item>
      <title>If TDD is Zen, adding Serverless brings Nirvana</title>
      <dc:creator>Jordi</dc:creator>
      <pubDate>Fri, 20 Jul 2018 10:21:51 +0000</pubDate>
      <link>https://dev.to/ledfusion/if-tdd-is-zen-adding-serverless-brings-nirvana-545c</link>
      <guid>https://dev.to/ledfusion/if-tdd-is-zen-adding-serverless-brings-nirvana-545c</guid>
      <description>&lt;h1&gt;
  
  
  If TDD is Zen, adding Serverless brings Nirvana
&lt;/h1&gt;

&lt;p&gt;Today we are going to build the dream backend for your startup with TDD and Serverless.&lt;/p&gt;

&lt;p&gt;Traditional API server have come a long way, but today’s fast moving projects need to kindly consider serverless, the sooner, the better.&lt;/p&gt;

&lt;p&gt;Being requirement #1 to &lt;em&gt;ship as soon as possible&lt;/em&gt;, the common side effect is that the codebase and infrastructure become harder to maintain once the product and the team grow.&lt;/p&gt;

&lt;p&gt;Serverless architectures help mitigating this situation in many ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Lambda functions encourage to write granular, clean and specific API operations.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;They also force to decouple code from the local architecture (url, ports, etc).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Combined with TDD, you can develop faster and leaner.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;No server means no sysadmin. More time to spend on your project.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In short, what we are about to explore is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A clean serverless API project&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ready for &lt;strong&gt;Test Driven Development&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Connecting to a &lt;strong&gt;cloud database&lt;/strong&gt; (MongoDB Atlas)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Using &lt;strong&gt;secret management&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;With &lt;strong&gt;automated deployment&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Using &lt;strong&gt;staging environments&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So let’s get right to it!&lt;/p&gt;

&lt;h2&gt;
  
  
  Start me up!
&lt;/h2&gt;

&lt;p&gt;First, make sure that you have &lt;a href="https://nodejs.org/en/"&gt;NodeJS&lt;/a&gt; on your system and install the Serverless framework:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm i -g serverless       (Windows)
sudo npm i -g serverless  (Linux and MacOS)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Cloud accounts
&lt;/h2&gt;

&lt;p&gt;Create an account on &lt;a href="https://aws.amazon.com/"&gt;Amazon Web Services&lt;/a&gt; and open the &lt;a href="https://console.aws.amazon.com/iam/home"&gt;IAM Management Console&lt;/a&gt; when you are done. You need to add a new user.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--damyPAA3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/4900/1%2AIH84jGRkzFyWKdZsZSz12g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--damyPAA3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/4900/1%2AIH84jGRkzFyWKdZsZSz12g.png" alt="Amazon AWS IAM Console"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Give it a meaningful name and enable “Programmatic access” for it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mBfHw088--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/4898/1%2A7lBKdnCA7hLl2BULeSYVwg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mBfHw088--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/4898/1%2A7lBKdnCA7hLl2BULeSYVwg.png" alt="IAM user creation"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By now, we are are just developing, so we will attach the &lt;em&gt;AdministratorAccess&lt;/em&gt; policy and keep working on the project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;IMPORTANT&lt;/strong&gt;: When the project is ready for production, get back to the lambda IAM user and &lt;a href="https://serverless.com/blog/abcs-of-iam-permissions/"&gt;check this article&lt;/a&gt; on how to apply the &lt;strong&gt;Least Privilege Principle&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---J7vz9pS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/4934/1%2AMSd-Sil0QCBuo378R-R3ng.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---J7vz9pS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/4934/1%2AMSd-Sil0QCBuo378R-R3ng.png" alt="AWS IAM user just created"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our AWS user is now ready. Copy the keys shown on the screen and run in the console:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;serverless config credentials --provider aws --key &amp;lt;the-access-key-id&amp;gt; --secret &amp;lt;the-secret-access-key&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Done! Your serverless environment is ready to connect to Amazon and do its magic. Now let’s a database to connect to.&lt;/p&gt;



&lt;p&gt;The choice of a particular database is out of the scope of this article. Because MongoDB is one of the most popular NoSQL databases, we will open an account at &lt;a href="https://www.mongodb.com/cloud/atlas"&gt;MongoDB Atlas&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--o09GMVfX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/4152/1%2Aqd23A2I_jheI47KCq34eLw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--o09GMVfX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/4152/1%2Aqd23A2I_jheI47KCq34eLw.png" alt="MongoDB Atlas"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select a provider and a region that suit your needs. Since our code will be running on AWS Lambda, it makes sense to select &lt;strong&gt;AWS&lt;/strong&gt; as the provider, and the same region where we will deploy our lambdas.&lt;/p&gt;

&lt;p&gt;Check any additional settings and choose a name for your cluster. Wait for a few minutes for it to provision.&lt;/p&gt;

&lt;p&gt;When it is ready, open the &lt;em&gt;Security&lt;/em&gt; tab of the cluster and add a new user.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UWn065tQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/4152/1%2ApPMg97sZuIvTGvEpqimjKQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UWn065tQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/4152/1%2ApPMg97sZuIvTGvEpqimjKQ.png" alt="MongoDB cluster just created"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since we are just testing, enter a username/password of your choice and select “&lt;em&gt;Read and write to any database”&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;IMPORTANT&lt;/strong&gt;: When you are ready for production, you should apply again the &lt;strong&gt;Least Privilege Principle&lt;/strong&gt; and restrict privileges to just your app’s database. You should also create different users for production and staging environments.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZkfZKsw3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/4152/1%2AqpVpCVio8g6ASjnxHISD6A.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZkfZKsw3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/4152/1%2AqpVpCVio8g6ASjnxHISD6A.png" alt="DB user creation"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Keep the user and password and get back to the cluster overview. Click on the &lt;em&gt;“Connect”&lt;/em&gt; button. Next we need to whitelist the IP addresses allowed to connect to the database. Unfortunately, on AWS Lambda we have no predictable way to determine the IP address that will connect to MongoDB Atlas. So the only choice is to go for &lt;em&gt;“Allow access from anywhere”.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eF0JwlPV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/4148/1%2AHW2PgbEME2_c-dZg1je_kg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eF0JwlPV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/4148/1%2AHW2PgbEME2_c-dZg1je_kg.png" alt="Connection settings"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, click on &lt;em&gt;“Connect your application”&lt;/em&gt;, choose version 3.6 and copy the URL string for later.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--99ehkUbG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2844/1%2A11iuBp6GGlSiPVN5GzEt1Q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--99ehkUbG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2844/1%2A11iuBp6GGlSiPVN5GzEt1Q.png" alt="Connection URL"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Let’s code!
&lt;/h2&gt;

&lt;p&gt;Enough accounts, now let’s get our hands dirty. Open the console and create a NodeJS project on folder &lt;code&gt;my-api&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;serverless create --template aws-nodejs --path my-api
cd my-api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Let’s invoke the default function:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;serverless invoke local -f hello

(output)
{
    "statusCode": 200,
    "body": "{\"message\":\"Go Serverless v1.0! Your function executed successfully!\",\"input\":\"\"}"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Ok, running. Let’s create the &lt;code&gt;package.json&lt;/code&gt; and add a few dependencies:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm init -y
npm i mongodb
npm i -D serverless-offline serverless-mocha-plugin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Next, let’s define our environment. The serverless CLI just created the file serverless.yml for us. Clean it up and edit serverless.yml like this:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;A few things to note here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;We are using NodeJS 8.10 in order to get the modern Javascript goodies.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We have defined the same region that we previously chose on MongoDB Atlas.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A hello function is added by default in &lt;code&gt;handlers.js&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We are adding two plugins at the bottom (from the dependencies we installed before). In a Daft Punk fashion, they will help us develop &lt;a href="https://www.youtube.com/watch?v=GDpmVUEjagg"&gt;better, faster, stronger&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now we can start listening for incoming HTTP requests on our local computer:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;serverless offline start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--b_o1Zlxm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2916/1%2AMCc-CwUegR0w1MRZI7gjxQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--b_o1Zlxm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2916/1%2AMCc-CwUegR0w1MRZI7gjxQ.png" alt="Serverless offline listening on port 3000"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So if we open our browser and visit &lt;a href="http://localhost:3000,"&gt;http://localhost:3000,&lt;/a&gt; we will get the output of the hello function, which is attached to the &lt;code&gt;/&lt;/code&gt; path by default. The output is a message, in addition to the HTTP headers and parameters that the function gets.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xxZ1y93d--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/4076/1%2AcMaRA4T1bD8t8HeZX5wJ0A.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xxZ1y93d--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/4076/1%2AcMaRA4T1bD8t8HeZX5wJ0A.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The cool thing is that if we edit handlers.js so that hello returns something different, you’ll notice that refreshing the browser will show the updated content. &lt;strong&gt;Live reload API&lt;/strong&gt; out of the box!&lt;/p&gt;

&lt;p&gt;Before we jump into our TDD environment, let’s tidy up a bit our project and arrange our files and routes.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir handlers test
rm handler.js
touch handlers/users.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;The routes we will be supporting are:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /users
GET /users/&amp;lt;id&amp;gt;
POST /users
PUT /users/&amp;lt;id&amp;gt;
DELETE /users/&amp;lt;id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;So let’s edit &lt;code&gt;serverless.yml&lt;/code&gt; and define them. Edit the &lt;code&gt;functions:&lt;/code&gt; block to contain these lines:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;As you can see, each key inside the functions block is the name of a Lambda function. Note that &lt;code&gt;handler: handlers/users.list&lt;/code&gt; would translate into: &lt;br&gt;
&lt;em&gt;Use the &lt;code&gt;list&lt;/code&gt; JS function inside &lt;code&gt;handlers/user.js&lt;/code&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Time to TDD!
&lt;/h2&gt;

&lt;p&gt;The serverless CLI provides a command to add new tests for each function. Let’s check it:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;serverless create test -f listUsers
serverless create test -f getUser
serverless create test -f addUser
serverless create test -f updateUser
serverless create test -f removeUser
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;The test folder now contains a dummy spec file for each of our Lambda functions. As all our user-related functions are pointing to &lt;code&gt;handlers/users.js&lt;/code&gt; maybe it’s better that the specs keep the same structure.&lt;/p&gt;

&lt;p&gt;So let’s discard the isolated specs &lt;code&gt;rm test/*&lt;/code&gt; , combine them into a single file and code the full user spec in &lt;code&gt;test/users.spec.js&lt;/code&gt;.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;As you can see, instead of importing just one function wrapper, we import them all and define test cases for the whole set.&lt;/p&gt;

&lt;p&gt;You may also note that we are not performing HTTP requests. Rather we have to pass the parameters as they would be received by the function. If you are interested to spec like an HTTP client, &lt;a href="https://github.com/activescott/serverless-http-invoker"&gt;check this library&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;What happens now if we run the test suite?&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;serverless invoke test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;You guessed it! Booom. And that’s because… we haven’t coded yet the handlers! But as you know: in TDD, specs are coded before the application logic.&lt;/p&gt;

&lt;p&gt;So, as our runtime is NodeJS 8, we can take advantage of ES6/ES7 features to write cleaner code in our lambda functions. Let’s implement the (still undefined) functions in &lt;code&gt;handlers/users.js&lt;/code&gt;:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;Note that unlike a traditional NodeJS server, the connection to the database needs to be opened and closed at each execution. This is due to the nature of how serverless works: there is no process running all the time. Instead, instances are created and destroyed when external events occur.&lt;/p&gt;

&lt;p&gt;Also note that all routes make sure to always close the DB connection. Otherwise, the internal NodeJS event loop would keep the process alive until the timeout was reached, and you might incur in higher charges.&lt;/p&gt;

&lt;p&gt;And finally, note that thanks to using NodeJS v8, we can return our response instead of using callbacks with error-first parameter.&lt;/p&gt;

&lt;p&gt;So now, our specs are ready, our implementation is there. The moment of truth:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KlZMHqrM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2904/1%2Ak1d_pxTzB0Q01yesHByY8Q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KlZMHqrM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2904/1%2Ak1d_pxTzB0Q01yesHByY8Q.png" alt="Test execution"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yay! Here is our first serverless API. As you may see, local execution times are not particularly impressive, but keep the following in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Our goal is not about single request speed. Rather, we aim for concurrent massive scalability, easy maintainability and future-proof code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Latencies are mainly due to the time spent by our local computer connecting to the remote database. Tests with 3~4 DB requests will experience much higher latencies than when code is running within the datacenter.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We are using the lightest implementation possible (&lt;code&gt;mongodb&lt;/code&gt; instead of &lt;code&gt;mongoose&lt;/code&gt;, and plain JS instead of Express/Connect &lt;a href="https://github.com/dougmoscrop/serverless-http"&gt;on top of Serverless&lt;/a&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If you switch to a local MongoDB server, running the tests will take around 80ms.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We’ll check performance back when our code is deployed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Secret management
&lt;/h2&gt;

&lt;p&gt;We are almost ready to deploy, but before we need to deal with an important aspect: &lt;strong&gt;keeping credentials out of the codebase&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Luckily for us, Serverless suports &lt;a href="https://eu-central-1.console.aws.amazon.com/systems-manager/parameters"&gt;Simple Systems Manager&lt;/a&gt; (SSM) since version 1.22. This means that we can store key/value data to our IAM user and have it automatically retrieved whenever Serverless needs to resolve a secret.&lt;/p&gt;

&lt;p&gt;So, first of all let’s get back to our &lt;code&gt;handlers/users.js&lt;/code&gt; file, copy the current URL string and replace:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const uri = "mongodb+srv://lambda:lambda@myapp...."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;with:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const uri = process.env.MONGODB_URL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Next, let’s add an environment block to provider in serverless.yml :&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;provider:
  name: aws
  runtime: nodejs8.10
  stage: prod
  region: eu-central-1
  environment: 
    MONGODB_URL: ${ssm:MY_API_MONGODB_URL~true}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;This will bind the &lt;code&gt;MONGODB_URL&lt;/code&gt; environment variable to the &lt;code&gt;MY_API_MONGODB_URL&lt;/code&gt; SSM key &lt;strong&gt;at deploy time&lt;/strong&gt; and &lt;code&gt;~true&lt;/code&gt; will decrypt the contents with the default IAM user's key.&lt;/p&gt;

&lt;p&gt;Finally, let’s grab the string we just copied and store the credential in our SSM:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install awscli    # install the AWS CLI if necessary
aws configure         # confirm the key/secret, define your region

aws ssm put-parameter --name MY_API_MONGODB_URL --type SecureString --value "mongodb+srv://lambda:xxxxxx@myapp-.....mongodb.net/my-app?retryWrites=true"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;If you are part of a bigger team, &lt;a href="https://serverless.com/blog/serverless-secrets-api-keys/"&gt;read here&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Deploying
&lt;/h2&gt;

&lt;p&gt;Stay with me, we are almost done! Let’s deploy our code to the cloud. We will update &lt;code&gt;serverless.yml&lt;/code&gt; before. Then:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;serverless deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--V5HCVMAY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2912/1%2AIqLS6ddC-DNpicBcO1tzlw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--V5HCVMAY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2912/1%2AIqLS6ddC-DNpicBcO1tzlw.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ready! You can also &lt;a href="https://eu-central-1.console.aws.amazon.com/lambda/home"&gt;manage them here&lt;/a&gt; (select the appropriate region).&lt;/p&gt;

&lt;p&gt;If you call the URL corresponding to the listUsers function you will see that the latency takes under one third of what it was taking from out computer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--i5HDlKdO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2916/1%2A-Ia0X-xiVMpNTcZ13TL0AQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--i5HDlKdO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2916/1%2A-Ia0X-xiVMpNTcZ13TL0AQ.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This happens because now, the lambda function and the DB server are in the same region (i.e. datacenter), so connection latencies between them are considerably lower. Our round trip to Amazon will always be there, but now DB connections will not.&lt;/p&gt;
&lt;h2&gt;
  
  
  Production
&lt;/h2&gt;

&lt;p&gt;As already commented during the article, when your API is ready to ship:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Remove the administrative permissions from your IAM user and &lt;a href="https://serverless.com/blog/abcs-of-iam-permissions/"&gt;refer to this page&lt;/a&gt; for insights on how to grant fine grained privileges.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Restrict the privileges of the DB user to only the database of your application, and not just any.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Deployments should only be made by project maintainers. The rest of developers shouldn’t need to configure any IAM Lambda credentials.&lt;/p&gt;
&lt;h2&gt;
  
  
  Cleaning
&lt;/h2&gt;

&lt;p&gt;Deploying a Lambda function will involve (at least) three different services from Amazon. If you intend to wipe an existing Lambda function you need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Delete the function from AWS Lambda&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Delete the corresponding bucket from S3&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Delete the corresponding stack from CloudFormation&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Wrap up
&lt;/h2&gt;

&lt;p&gt;I hope you enjoyed reading the article as much as I enjoyed writing it. If you want more, heart, unicorn, comment, share and smile 🙂!&lt;/p&gt;

&lt;p&gt;If you want to experiment with the code of the article, feel free to clone the starter repo from GitHub: &lt;a href="https://github.com/ledfusion/serverless-tdd-starter/tree/part-1"&gt;https://github.com/ledfusion/serverless-tdd-starter/tree/part-1&lt;/a&gt;&lt;br&gt;
BTW: I am available to help you grow your projects. &lt;br&gt;
Feel free to find me on &lt;a href="https://jordi-moraleda.github.io/"&gt;https://jordi-moraleda.github.io/&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Bonus track #1: Staging
&lt;/h2&gt;

&lt;p&gt;If you are like most of us, you will need at least 3 environments for your project: development, staging and production.&lt;/p&gt;

&lt;p&gt;For the database, developers could use a local instance of MongoDB to speed connections up, but for staging and production, we need to provide completely independent database environments.&lt;/p&gt;

&lt;p&gt;Head back to MongoDB Atlas and create two different user accounts (my-app-prod and my-app-staging ) with access to two different databases (my-app-prod and my-app-staging respectively).&lt;/p&gt;

&lt;p&gt;Let’s remove the key we created before:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws ssm delete-parameter --name MY_API_MONGODB_URL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;And create two, for production and staging:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# PROD
aws ssm put-parameter --name MY_API_MONGODB_URL_prod --type SecureString --value "mongodb+srv://my-app-prod:xxxxxx@myapp-.....mongodb.net/my-app-prod?retryWrites=true"

# STAGING
aws ssm put-parameter --name MY_API_MONGODB_URL_staging --type SecureString --value "mongodb+srv://my-app-staging:xxxxxx@myapp-.....mongodb.net/my-app-staging?retryWrites=true"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a href="https://eu-central-1.console.aws.amazon.com/systems-manager/parameters/"&gt;Here they are&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Pf9kj3jX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/5104/1%2AeOCDnTSicnPfhr47nBpDMQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Pf9kj3jX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/5104/1%2AeOCDnTSicnPfhr47nBpDMQ.png" alt="AWS Systems Manager"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now let’s edit &lt;code&gt;serverless.yml&lt;/code&gt; &amp;gt; &lt;code&gt;provider&lt;/code&gt; and let’s do some magic:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;First of all, the stage field tells Serverless where to deploy the lambdas. If you deploy like below:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;serverless deploy --stage prod
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Then, &lt;code&gt;provider.stage&lt;/code&gt; would be &lt;code&gt;"prod"&lt;/code&gt; . If we just ran &lt;code&gt;serverless deploy&lt;/code&gt; then &lt;code&gt;provider.stage&lt;/code&gt; would default to &lt;code&gt;"dev"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Second, the value of &lt;code&gt;MONGODB_URL&lt;/code&gt; is evaluated in 2 steps, depending on the environment. If we are on &lt;code&gt;"prod"&lt;/code&gt; or &lt;code&gt;"staging"&lt;/code&gt;, Serverless will fetch &lt;code&gt;MY_API_MONGODB_URL_prod&lt;/code&gt; or &lt;code&gt;MY_API_MONGODB_URL_staging&lt;/code&gt; respectively from SSM and use the value. If our IAM user does not have that key, &lt;code&gt;MONGODB_URL&lt;/code&gt; will default to &lt;code&gt;"mongodb://localhost:27017"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Ta da! This allows our development team to code and test from their local database, while code running on AWS will get the remote URL connection string.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OvIiAoSb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2916/1%2APr2rvfqZF827Lg9H3LMl1A.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OvIiAoSb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2916/1%2APr2rvfqZF827Lg9H3LMl1A.png" alt="Deployment to staging"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here they are, so staging and prod are ready to go!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HbXmG9Cm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/3372/1%2ACbSt2cJmzTrgJmVhrW-zVQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HbXmG9Cm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/3372/1%2ACbSt2cJmzTrgJmVhrW-zVQ.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus track #2: Automated tasks
&lt;/h2&gt;

&lt;p&gt;As the title suggests, we are aiming for nirvana, not just zen. Having to type repetitive commands may be a bit of overhead, so let’s finish our show with a clean set of commands to work with the project.&lt;/p&gt;

&lt;p&gt;As a recap, the actions we may perform during the lifecycle of the project are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Run the app locally&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Run the test suite&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Run the test suite and deploy to staging if successful&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Run the test suite and deploy to production if successful&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So let’s define these actions on our package.json :&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
   "scripts": {
    "deploy": "npm test &amp;amp;&amp;amp; sls deploy --stage staging",
    "deploy:prod": "npm test &amp;amp;&amp;amp; sls deploy --stage prod",
    "start": "serverless offline start",
    "test": "serverless invoke test"
  },
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now the team can run the API with &lt;code&gt;npm start&lt;/code&gt; and test with &lt;code&gt;npm test&lt;/code&gt;, while the project maintainer can deploy to staging with &lt;code&gt;npm run deploy&lt;/code&gt; and to production with &lt;code&gt;npm run deploy:prod&lt;/code&gt;. Nobody will interfere with unintended settings, data or environments.&lt;/p&gt;

&lt;p&gt;After this whole exercise of integrations, you should have an easy to code, test, upgrade and maintain backend. It should work well with agile teams and fast moving companies and I hope that it does for you!&lt;/p&gt;

&lt;p&gt;Thanks for your read.&lt;/p&gt;




&lt;p&gt;If you liked it, do not miss &lt;a href="https://dev.to/ledfusion/if-tdd-is-zen-adding-serverless-brings-nirvana---part-2-f0"&gt;the part #2 of this article&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This post was originally posted on &lt;a href="https://medium.com/@ledfusion/tdd-means-zen-along-with-serverless-means-nirvana-a39a76ee8e63"&gt;medium.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@marion_michele"&gt;Marion Michele&lt;/a&gt; on &lt;a href="https://unsplash.com"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>serverless</category>
      <category>aws</category>
      <category>lambda</category>
      <category>tdd</category>
    </item>
  </channel>
</rss>
