<?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: Sc0ra</title>
    <description>The latest articles on DEV Community by Sc0ra (@sc0ra).</description>
    <link>https://dev.to/sc0ra</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%2F461018%2F7698ba3d-db39-4319-9e56-4d1308f96310.jpg</url>
      <title>DEV Community: Sc0ra</title>
      <link>https://dev.to/sc0ra</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sc0ra"/>
    <language>en</language>
    <item>
      <title>Serverless Contracts to the Rescue of your Fullstack Application</title>
      <dc:creator>Sc0ra</dc:creator>
      <pubDate>Tue, 22 Nov 2022 13:00:55 +0000</pubDate>
      <link>https://dev.to/slsbytheodo/serverless-contracts-to-the-rescue-of-your-fullstack-application-3f87</link>
      <guid>https://dev.to/slsbytheodo/serverless-contracts-to-the-rescue-of-your-fullstack-application-3f87</guid>
      <description>&lt;h2&gt;
  
  
  Clear and steady interfaces for stable applications 🧘
&lt;/h2&gt;

&lt;p&gt;Introducing a &lt;strong&gt;breaking change&lt;/strong&gt; in the input or output of your API and not updating the frontend client might be the most common error a fullstack developper makes at the start of its career (and even later 👴).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Interfaces&lt;/strong&gt; between different services are one of the weakest, if not the weakest, point in an application (right after copy pasted stackoverflow one liners 👨‍🔬).&lt;/p&gt;

&lt;p&gt;Thats why you should put a lot of effort in defining those interfaces. However, it &lt;strong&gt;can become quite repetitive&lt;/strong&gt;, and that's what we wanted to tackle with &lt;strong&gt;Serverless contracts&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Serverless Contracts 📑
&lt;/h2&gt;

&lt;p&gt;At &lt;a href="https://dev.to/kumo"&gt;Kumo&lt;/a&gt;, we build &lt;strong&gt;fullstack&lt;/strong&gt; applications using &lt;strong&gt;Serverless&lt;/strong&gt; and &lt;strong&gt;TypeScript&lt;/strong&gt;. During our multiple projects, we encountered the problem of varying interfaces a lot. That is why we decided to build &lt;a href="https://www.swarmion.dev/" rel="noopener noreferrer"&gt;Swarmion&lt;/a&gt;, a tool to build stable serverless application at scale.&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%2F5boqk5aax3cb9v2lfzqr.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%2F5boqk5aax3cb9v2lfzqr.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One of its most important feature is Serverless Contracts.&lt;/p&gt;




&lt;h2&gt;
  
  
  What we wanted 🙏
&lt;/h2&gt;

&lt;p&gt;When you want to build a tech tool, you should keep a &lt;strong&gt;product approach&lt;/strong&gt; as much as possible. It can help you defining where the value sits and where you should be putting work.&lt;/p&gt;

&lt;p&gt;So when we first thought about contracts, we focused on our main use case: &lt;strong&gt;interfaces&lt;/strong&gt; between frontend and backend microservices, with the backend deployed behind API Gateway.&lt;/p&gt;

&lt;p&gt;With that in mind, we wanted to address the folowing pain points with Serverless Contracts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Usable in both the &lt;strong&gt;backend&lt;/strong&gt; and the &lt;strong&gt;frontend&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strong type safety&lt;/strong&gt; during static analysis&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IntelliSense&lt;/strong&gt; in the IDE&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime input and output validation&lt;/strong&gt; at the backend for client-side errors handling&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reduced boilerplate&lt;/strong&gt; in provider (API) and consumer (frontend) for every handler/request&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Human readable&lt;/strong&gt; definition&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What we built 🔨
&lt;/h2&gt;

&lt;p&gt;Now, lets dive into some code, with your first &lt;strong&gt;Serverless contract&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// contracts/users-contracts/src/contracts/getUser/contract.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;ApiGatewayContract&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;@swarmion/serverless-contracts&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="err"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;pathParametersSchema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userEntitySchema&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;./schemas&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;getUserContract&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ApiGatewayContract&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;users-getUser&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;integrationType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;httpApi&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;authorizerType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&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="s1"&gt;/users/{userId}&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="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="nx"&gt;pathParametersSchema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;queryStringParametersSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headersSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;bodySchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;outputSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userEntitySchema&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="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;getUserContract&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// contracts/users-contracts/src/contracts/getUser/schemas.ts&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;pathParametersSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&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;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;userId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;additionalProperties&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="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;const&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;userEntitySchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;userName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&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;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;userId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;userName&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;additionalProperties&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="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is a lot of things to see here:&lt;/p&gt;

&lt;h3&gt;
  
  
  The getUserContract 👥
&lt;/h3&gt;

&lt;p&gt;This class instance contains all the informations defining your endpoint interface:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Implementation details&lt;/strong&gt; of your endpoint:

&lt;ul&gt;
&lt;li&gt;A unique identifier&lt;/li&gt;
&lt;li&gt;The api gateway type (V1 or V2)&lt;/li&gt;
&lt;li&gt;The authorizer type (cognito, jwt, lambda or none)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;The &lt;strong&gt;path and method&lt;/strong&gt; of your endpoint&lt;/li&gt;

&lt;li&gt;The different parts of the &lt;strong&gt;incoming request&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;path parameters&lt;/li&gt;
&lt;li&gt;query string parameters&lt;/li&gt;
&lt;li&gt;headers&lt;/li&gt;
&lt;li&gt;body&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;The format of the &lt;strong&gt;output&lt;/strong&gt; of your endpoint.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;The last 2 categories are defined using JSON Schemas.&lt;/p&gt;

&lt;h3&gt;
  
  
  JSON Schemas 💽
&lt;/h3&gt;

&lt;p&gt;We use &lt;strong&gt;JSON Schemas&lt;/strong&gt; for multiple reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;They are a well known interface definition format in the &lt;strong&gt;javascript ecosystem&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;With the &lt;a href="https://github.com/ThomasAribart/json-schema-to-ts" rel="noopener noreferrer"&gt;json-schema-to-ts&lt;/a&gt; lib, we can define our &lt;strong&gt;TypeScript&lt;/strong&gt; types directly from those schemas without any duplication and provide &lt;strong&gt;fully typed&lt;/strong&gt; helpers as we will see later.&lt;/li&gt;
&lt;li&gt;They allow us to perform &lt;strong&gt;run time validation&lt;/strong&gt; with &lt;a href="https://ajv.js.org/" rel="noopener noreferrer"&gt;ajv&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this contract, we define an &lt;code&gt;outputSchema&lt;/code&gt; and a &lt;code&gt;pathParameterSchema&lt;/code&gt;. The other options are left undefined because those inputs are not used by our endpoint.&lt;/p&gt;

&lt;p&gt;Now that we are settled with our contract, we can start using it in our application&lt;/p&gt;

&lt;h2&gt;
  
  
  Provider-side features
&lt;/h2&gt;

&lt;p&gt;First, we want to define our endpoint using our contract.&lt;br&gt;
With the &lt;a href="https://www.serverless.com/" rel="noopener noreferrer"&gt;Serverless framework&lt;/a&gt;, you do it by defining 2 elements: the function &lt;strong&gt;trigger&lt;/strong&gt; and its &lt;strong&gt;handler&lt;/strong&gt;.  Serverless contracts provide helpers for both.&lt;/p&gt;
&lt;h3&gt;
  
  
  getTrigger ⚡
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// config.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;getTrigger&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;@swarmion/serverless-contracts&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;getUserContract&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&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;@my-app/contracts/getUserContract&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="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;getHandlerPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;getTrigger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;getUserContract&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;This method defines the path and method that will trigger the lambda based on the contract information.&lt;/p&gt;

&lt;p&gt;If you define an authorizer in your contract, you can provide it there directly, and it will &lt;strong&gt;prevent you to overwrite the trigger values&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// config.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;getTrigger&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;@swarmion/serverless-contracts&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;getUserContract&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&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;@my-app/contracts/getUserContract&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="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;getHandlerPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nf"&gt;getTrigger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;getUserContract&lt;/span&gt;&lt;span class="p"&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="s1"&gt;delete&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// ts will throw an error because 'delete' != 'get'&lt;/span&gt;
            &lt;span class="na"&gt;authorizer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;arn::aws...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="c1"&gt;// will also throw here because we did not &lt;/span&gt;
                                                                &lt;span class="c1"&gt;// define an authorizer in this contract&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;h3&gt;
  
  
  getHandler ⚙️
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// handler.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;getHandler&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;@swarmion/serverless-contracts&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;getUserContract&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&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;@my-app/contracts/getUserContract&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;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;getUserContract&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;event&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;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathParameters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// will have type 'string'&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;toto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// will fail type checking&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;pathParameters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// will also fail&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;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;123456&lt;/span&gt;&lt;span class="dl"&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="s1"&gt;Doe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt; &lt;span class="c1"&gt;// also type-safe!&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;default&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;getHandler&lt;/code&gt; helpers does multiple things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Full typing&lt;/strong&gt; for input and output in IDE&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Body parsing&lt;/strong&gt; and output serialization&lt;/li&gt;
&lt;li&gt;HTTP &lt;strong&gt;errors handling&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;input and output &lt;strong&gt;runtime validation&lt;/strong&gt; using ajv&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Consumer features
&lt;/h2&gt;

&lt;p&gt;Once the endpoint is defined, we want to request it from another service (a &lt;strong&gt;frontend application&lt;/strong&gt; for example).&lt;br&gt;
To do so, you can use consumer helpers to generate type safe requests.&lt;/p&gt;

&lt;h3&gt;
  
  
  getFetchRequest 🌐
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// useGetUser.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;getFetchRequest&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;@swarmion/serverless-contracts&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;getUserContract&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&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;@my-app/contracts/getUserContract&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getFetchRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;getUserContract&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;pathParameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;15&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c1"&gt;// correctly typed&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-header&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hello&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// will fail type checking&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://my-app.com&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Conclusion ⭐
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Swarmion Serverless contracts helps you build scalable applications with multiple services and strong interfaces between them.&lt;/strong&gt; We presented you API Gateway contracts but we also recently implemented EventBridge contracts for message interfaces between services.&lt;/p&gt;

&lt;p&gt;Also, Swarmion comes with more features, including a project and service generator which implements all the best practices we gathered at Kumo.&lt;/p&gt;

&lt;p&gt;You can check our &lt;a href="https://www.swarmion.dev/" rel="noopener noreferrer"&gt;developer guide here&lt;/a&gt;, everything is open source, feel free to contact us and contribute!&lt;/p&gt;

</description>
      <category>serverless</category>
      <category>typescript</category>
      <category>aws</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Prevent AWS from Reading Your Step Functions Data</title>
      <dc:creator>Sc0ra</dc:creator>
      <pubDate>Mon, 31 Aug 2020 14:24:24 +0000</pubDate>
      <link>https://dev.to/sc0ra/prevent-aws-from-reading-your-step-functions-data-10m</link>
      <guid>https://dev.to/sc0ra/prevent-aws-from-reading-your-step-functions-data-10m</guid>
      <description>&lt;p&gt;&lt;strong&gt;AWS Step Functions&lt;/strong&gt; is the perfect tool to handle long-running, complex or business-critical workflows such as payment or subscription flows. However, a naive implementation could &lt;strong&gt;put sensitive data at risk&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is AWS Step Functions?
&lt;/h2&gt;

&lt;p&gt;AWS Step Functions is an &lt;strong&gt;Amazon cloud service&lt;/strong&gt; designed to create state machines to orchestrate multiple Lambda functions, branching logic and external services in one place. It presents most of the advantages of the &lt;a href="https://blog.theodo.com/2020/06/go-serverless/"&gt;serverless services&lt;/a&gt;: the scaling is immediate and the pricing is based only on the computing time and the number of step transitions.&lt;/p&gt;

&lt;p&gt;The state machines can be defined directly in the AWS Console with a JSON configuration and you directly have a graph representation of your workflow.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jwQ2cq8I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/qbuforir8vi0631aoewb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jwQ2cq8I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/qbuforir8vi0631aoewb.png" alt="AWS Step Functions state diagram" width="374" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;AWS Step Functions state diagram&lt;/p&gt;



&lt;p&gt;At Theodo, we use &lt;a href="https://blog.theodo.com/2020/04/serverless-framework-partnership/"&gt;Serverless&lt;/a&gt; as our framework with the Step Functions plugin to write our AWS Lambda functions, define our resources (DynamoDB and Queues) and setup AWS Step Functions and the state machines directly in our codebase.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example: an identity verification workflow with Step Functions
&lt;/h2&gt;

&lt;p&gt;The previous picture shows an identification verification workflow. It is a piece of a bigger validation flow for banking applications.&lt;/p&gt;

&lt;p&gt;What it does is request an identification URL to a third-party system, send this link by text message to the applicant and wait for them to complete the identity verification.&lt;br&gt;
When it is done, the third-party system calls a webhook, which restarts the state machine and either triggers a retry, sends a rejection email, or continues the global validation process.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SD8gaV47--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/8aeedxnioxktxyebd487.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SD8gaV47--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/8aeedxnioxktxyebd487.png" alt="Graphical representation of the identification workflow" width="740" height="558"&gt;&lt;/a&gt;&lt;/p&gt;

The identification flow



&lt;p&gt;To perform all of those actions, &lt;strong&gt;sensitive information&lt;/strong&gt; will flow through the inputs and outputs of the state machine steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;👤 Personal information from the applicant which are sent to the third-party service to check his/her identity&lt;/li&gt;
&lt;li&gt;📱 The phone number of the applicant to send the SMS&lt;/li&gt;
&lt;li&gt;✉️ The email address of the applicant to send the mail&lt;/li&gt;
&lt;li&gt;✔️ The result of the identity verification&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Problem: we are exposing sensitive data
&lt;/h2&gt;

&lt;p&gt;With this implementation, we see that sensitive data flow through the inputs and outputs of your state machine directly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;state-machine.yaml&lt;/span&gt;
&lt;span class="s"&gt;----------&lt;/span&gt;

&lt;span class="na"&gt;SendIdentificationRequest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Task&lt;/span&gt;
  &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;arn:aws:states:::lambda:invoke&lt;/span&gt;
  &lt;span class="na"&gt;Parameter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;FunctionName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="s"&gt;Fn::ImportValue: ${self:provider.stage}-sendSMS-Function-arn&lt;/span&gt;
    &lt;span class="na"&gt;Payload&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;creditApplicationId.$&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$.creditApplicationId&lt;/span&gt;
      &lt;span class="na"&gt;creditApplicant.$&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$.creditApplicant&lt;/span&gt;
      &lt;span class="na"&gt;phoneNumber.$&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$.phoneNumber&lt;/span&gt;
      &lt;span class="na"&gt;message.$&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$.smsBody&lt;/span&gt;
  &lt;span class="na"&gt;ResultPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$.identificationSMS&lt;/span&gt;
  &lt;span class="na"&gt;Next&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;WaitIdentification&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An exemple of sensitive data flowing through our inputs and outputs&lt;/p&gt;



&lt;p&gt;The problem is: nowadays, most DevOps engineer, developers and even some product managers with access to AWS and can see that information. Not very &lt;strong&gt;GDPR&lt;/strong&gt; friendly, right? Furthermore, AWS Lambda dumps are stored in an S3 bucket which is not encrypted with the default configuration. Any malicious access to your AWS stack could &lt;strong&gt;leak sensitive information&lt;/strong&gt; far more easily than by corrupting your database for example. The more you spread sensitive information between multiple services, the more you put yourself at risk. Finally, as it is stated in &lt;a href="https://docs.aws.amazon.com/step-functions/latest/dg/data-protection.html"&gt;AWS documentation&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Any data that you enter into Step Functions or other services might get picked up for inclusion in diagnostic logs.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With that in mind, you may completely dump AWS Step Functions, or you may try to find a solution because the tool is still awesome and perfectly suits your needs!&lt;/p&gt;

&lt;h2&gt;
  
  
  Our solution
&lt;/h2&gt;

&lt;p&gt;To prevent leaking sensitive information, we need to clean every step function input and output. Here are two ways of doing it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep the data in the input and output but encrypt and decrypt it between each step&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never transmit sensitive data between steps&lt;/strong&gt; and retrieve it directly during their execution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both solutions are viable. However, we prefered the second one because we wanted to keep our sensitive data in a single safe spot, where we put a lot of attention to the security.&lt;/p&gt;

&lt;p&gt;We were already using &lt;strong&gt;AWS DynamoDB&lt;/strong&gt; for the waiting tokens of our lambda functions, so we decided to create a new table to store sensitive data between steps.&lt;/p&gt;

&lt;p&gt;We configured it like below, using an external KMS key.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;resources.yaml&lt;/span&gt;
&lt;span class="s"&gt;----------&lt;/span&gt;

&lt;span class="na"&gt;StepSentiveInputTable&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::DynamoDB::Table&lt;/span&gt;
  &lt;span class="na"&gt;DeletetionPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Retain&lt;/span&gt;
  &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;TableName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;step-sensitive-input-table&lt;/span&gt;
    &lt;span class="na"&gt;AttributeDefinitions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;AttributeName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sensitiveInputId&lt;/span&gt;
        &lt;span class="na"&gt;AttributeType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;S&lt;/span&gt;
    &lt;span class="na"&gt;KeySchema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;AttributeName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sensitiveInputId&lt;/span&gt;
        &lt;span class="na"&gt;KeyType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HASH&lt;/span&gt;
    &lt;span class="na"&gt;BillingMode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PAY_PER_REQUEST&lt;/span&gt;
    &lt;span class="na"&gt;PointInTimeRecoverySpecification&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;PointINTimeRecoveryEnabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;SSESpecification&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;SSEEnabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;SSEType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;KMS&lt;/span&gt;
      &lt;span class="na"&gt;KeyId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hsm-key-id&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;DynamoDB table definition in Serverless&lt;/p&gt;



&lt;p&gt;We were already using DynamoDB from our lambdas to save our waiting task tokens and we chose to add another table to save sensitive data between steps. Furthermore, on AWS, you can choose to save the encryption keys of your DynamoDB tables outside of AWS, which was a big plus for us.&lt;/p&gt;

&lt;p&gt;To avoid repeating ourselves, we coded a &lt;strong&gt;wrapper&lt;/strong&gt; that we use on all our lambdas. It does the following things:&lt;/p&gt;

&lt;p&gt;After the return of the lambda, the wrapper:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Looks for sensitive data keys in the output, remove them and build an object with that information&lt;/li&gt;
&lt;li&gt;Save the sensitive data objects in a dynamo entry&lt;/li&gt;
&lt;li&gt;Add the id of the new dynamo entry under a “sensitiveData” key in the output&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before the execution of the main lambda function, the wrapper:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Looks for “sensitiveData” keys in the input&lt;/li&gt;
&lt;li&gt;Retrieve the content at the given IDs&lt;/li&gt;
&lt;li&gt;Replaces the "sensitiveData" keys with the resulting object in the input
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// encryption-wrapper.ts&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lambdaHandler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;LambdaHandler&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;LambdaHandler&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;decryptHandler&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="na"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&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;decryptedInformation&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;getSensitiveData&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;sensitiveInputId&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;lambdaHandler&lt;/span&gt;&lt;span class="p"&gt;({&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="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;decryptedInformation&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;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&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;clearOutput&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;decryptHandler&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sensitiveInputId&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;storeSensitiveData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;clearOutput&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;for&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;sensitiveKey&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;sensitiveInputKeys&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="nx"&gt;clearOutput&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;sensitiveKey&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="nx"&gt;sensitiveInputId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;clearOutput&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;Our custom encryption wrapper&lt;/p&gt;



&lt;p&gt;Last of all, we needed to encrypt the first input at the start of our state machine from the API to finally secure our users information all the way!&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Take care about what information you have in your lambdas and state machine events on AWS&lt;/li&gt;
&lt;li&gt;Always think twice about the data you put in your logs&lt;/li&gt;
&lt;li&gt;Spread your secrets and security implementation in as few services as possible&lt;/li&gt;
&lt;li&gt;Use a wrapper if you want to repeat behavior in multiple lambda functions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thank you very much for reading my article. Feel free to comment, ask for implementation details, and feel free to share the problems that you encountered with AWS Step Functions.&lt;/p&gt;

</description>
      <category>serverless</category>
      <category>aws</category>
      <category>security</category>
      <category>typescript</category>
    </item>
  </channel>
</rss>
