<?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: starpebble</title>
    <description>The latest articles on DEV Community by starpebble (@starpebble).</description>
    <link>https://dev.to/starpebble</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%2F313228%2F13c8e071-775d-48c9-8f23-cc004743bde1.png</url>
      <title>DEV Community: starpebble</title>
      <link>https://dev.to/starpebble</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/starpebble"/>
    <language>en</language>
    <item>
      <title>Google Search Dark Theme</title>
      <dc:creator>starpebble</dc:creator>
      <pubDate>Sat, 03 Jul 2021 23:16:31 +0000</pubDate>
      <link>https://dev.to/starpebble/google-search-dark-theme-1fi6</link>
      <guid>https://dev.to/starpebble/google-search-dark-theme-1fi6</guid>
      <description>&lt;p&gt;Here is a nice way to save energy - search in the dark theme.  Google's search service has a fantastic setting - Dark theme.  Turn it on.  It's fun.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5OFj-7gq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mo3ruevzjk2grwgalh58.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5OFj-7gq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mo3ruevzjk2grwgalh58.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Visit Google&lt;/li&gt;
&lt;li&gt;Search for 'Dark Mode'&lt;/li&gt;
&lt;li&gt;Click the settings icon on the right&lt;/li&gt;
&lt;li&gt;Toggle 'Dark theme' to On&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Climate change is no hoax.  Maybe you are searching for ways to save energy.  Saving energy is a simple way to stand up for cleaner computing.  Dark mode is showing up all over the web. It's a popular one. Please leave a comment if you have other nice suggestions to reduce the amount of energy used by a computer. I thank you.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>What Matters in a Conditional Cloud</title>
      <dc:creator>starpebble</dc:creator>
      <pubDate>Wed, 03 Feb 2021 17:11:27 +0000</pubDate>
      <link>https://dev.to/starpebble/what-matters-in-a-conditional-cloud-4e2e</link>
      <guid>https://dev.to/starpebble/what-matters-in-a-conditional-cloud-4e2e</guid>
      <description>&lt;p&gt;The cloud is becoming even more conditional.  We know the cloud is programmable.  This is exactly why it is very flexible.  The conditional cloud is a hot topic because it is showing up in more than one way.  Here are exactly two examples of the conditional cloud:&lt;/p&gt;

&lt;h3&gt;
  
  
  Conditional Access - Machine Learning
&lt;/h3&gt;

&lt;p&gt;A single mobile user accesses three private applications over the Internet.  The three applications are hosted in a private data center with an excellent source of carbon-free energy.  Azure AD conditionally grants access each time the user accesses any of the mobile applications.  For example, Azure's machine learning considers the user, location, device, and real-time risks.  Conditionally, the user may just get the web page.  Or, conditionally, the user may be asked to authenticate a second time, like a re-certification.  The decision is up to the cloud. &lt;a href="https://docs.microsoft.com/en-us/azure/active-directory/conditional-access/overview"&gt;learn more here&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Conditional Access - Rules Based
&lt;/h3&gt;

&lt;p&gt;A single developer has a closed serverless application limited to an exact number of accounts.  Each exact account number is listed in the S3 Bucket resource policy. It is a stated condition. The AWS Serverless Repository grants a user access to the application only when the user's deployment passes the condition where the SourceAccount number is in the list. &lt;a href="https://docs.aws.amazon.com/serverlessrepo/latest/devguide/serverlessrepo-how-to-publish.html#publishing-application-through-aws-console"&gt;learn more here&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What matters is up to you
&lt;/h3&gt;

&lt;p&gt;Don't forget time-based conditional access.  Sometimes temporary access is a stated condition.  Like for a hotfix that comes up in an emergency. One person may just need a few hours of access to a single exact machine in the cloud.  To keep it safe, a person can write one Common Expression Language statement in a Google Policy restricting access by the request time. The condition is the time. &lt;a href="https://cloud.google.com/iam/docs/reference/rest/v1/Policy"&gt;learn more here&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Identity Matters
&lt;/h3&gt;

&lt;p&gt;Conditions look fine-grained.  The conditions consider identity.  Nobody wants to look over a user's shoulders.  Please let me emphasize the conditional cloud is intended to make the cloud safer.  How the cloud exactly does this for us, either machine learning or rules-based, is a hot topic.  Any comments?&lt;/p&gt;

</description>
      <category>azure</category>
      <category>aws</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Authenticating APIs with Cognito</title>
      <dc:creator>starpebble</dc:creator>
      <pubDate>Mon, 24 Aug 2020 15:46:20 +0000</pubDate>
      <link>https://dev.to/starpebble/authenticating-apis-with-cognito-5fl3</link>
      <guid>https://dev.to/starpebble/authenticating-apis-with-cognito-5fl3</guid>
      <description>&lt;p&gt;The world is changing fast.  It feels like the whole world is shifting towards standards like GraphQL.  API Gateway isn't the only way to host an API.  I have a feeling that AppSync is rising as a possible serverless API host.  Is there a future proof way of authenticating today?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Authentication Methods&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's quickly list the possible authentication types.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;JSON Web Token (JWT)&lt;/li&gt;
&lt;li&gt;OAuth 2.0&lt;/li&gt;
&lt;li&gt;OpenID (OIDC)&lt;/li&gt;
&lt;li&gt;API Gateway API Key&lt;/li&gt;
&lt;li&gt;AWS Lambda Authorizer&lt;/li&gt;
&lt;li&gt;Identity and Access Management (IAM)&lt;/li&gt;
&lt;li&gt;Cognito User Pool&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This list answers a question like "How does a particular API authenticate a user?"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Serverless API Types&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's also list the types of serverless APIs.  The popular pattern of API Gateway REST + Lambda isn't the only way anymore.  There's a shift.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;AppSync&lt;/li&gt;
&lt;li&gt;API Gateway HTTP&lt;/li&gt;
&lt;li&gt;API Gateway REST&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This list answers a question like "What serverless API host serves a particular API on the Internet?"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Future Proof&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There's uncertainty in what users may demand.  So there is implied uncertainty in how to offer services to users.  The serverless wagon hasn't fallen off the trail just yet.  There is momentum in the resource sharing model that a serverless model offers.  Is there a future proof approach?&lt;/p&gt;

&lt;p&gt;I like any approach that is flexible.  AWS Cognito is a point of leverage for flexibility.  Here's why I believe Cognito can help future proof. Cognito can generate JWT tokens - &lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-vs-rest.html"&gt;API Gateway accepts&lt;/a&gt; them.  Cognito can generate OpenID OIDC tokens - &lt;a href="https://docs.aws.amazon.com/appsync/latest/devguide/security.html"&gt;AppSync accepts&lt;/a&gt; them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nifty Approaches&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's not forget to discuss some of the nifty new things.  Consider the passwordless login.  Now I'm not thinking about biometrics. I'm simply referring to logging in without a password.  Here's the approach: the user asks for a temporary login to be sent to something the user has.  Like an exact email address.  Or a specific phone number.  The user authenticates with the temporary login.  This is really nifty!  It's also an example of shift that wasn't predictable.  Which is why a flexible approach to authentication has many benefits, one of which is simplifying adaption to future users.&lt;/p&gt;

</description>
      <category>security</category>
      <category>aws</category>
      <category>serverless</category>
    </item>
    <item>
      <title>Simplify a Lambda dependency with a Lambda Layer</title>
      <dc:creator>starpebble</dc:creator>
      <pubDate>Mon, 17 Aug 2020 22:07:56 +0000</pubDate>
      <link>https://dev.to/starpebble/simplify-a-lambda-dependency-with-a-lambda-layer-3nke</link>
      <guid>https://dev.to/starpebble/simplify-a-lambda-dependency-with-a-lambda-layer-3nke</guid>
      <description>&lt;p&gt;Faster code deployments are better.  A Lambda layer can reduce the time to deploy a Lambda function.  A Lambda layer is simply a support layer to store code.  Here's how the layer speeds a deployment up, code in a Lambda layer may be excluded from the Lambda zip file. It's that easy. Smaller Lambda zip files are the pathway to Lambda rock stardom! 🤩&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Start
&lt;/h2&gt;

&lt;p&gt;Let's example how to quickly create a Lambda for the Python 3.7 runtime engine.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a &lt;a href="https://pip.pypa.io/en/stable/user_guide/#requirements-files"&gt;requirements.txt&lt;/a&gt; file listing one or more Python package dependencies&lt;/li&gt;
&lt;li&gt;Download the dependencies to a local disk with &lt;code&gt;pip install&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Archive the dependencies into a single zip file&lt;/li&gt;
&lt;li&gt;Create a single Lambda layer in the AWS Console, uploading the zip file artifact to the console&lt;/li&gt;
&lt;li&gt;Add the single Lambda layer in the AWS console to list of layers for the Lambda function&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Tips
&lt;/h2&gt;

&lt;p&gt;The Lambda community is simplifying local install of tools like 'pip' or 'aws cli'.  It's a long time in the making.  No longer must we install tools.  Docker containers with tools are available.  Here are two containers to consider for this quick start.  The containers are optional.  The containers simply save time. It's up to you.&lt;/p&gt;

&lt;h4&gt;
  
  
  LambCI
&lt;/h4&gt;

&lt;p&gt;&lt;a href="http://lambci.org/"&gt;LambCI&lt;/a&gt; is on a mission to improve serverless computing integration.  The docker container &lt;code&gt;lambci/lambda:build-python3.7&lt;/code&gt; has a perfect installation of &lt;code&gt;pip&lt;/code&gt;. Run step two with &lt;code&gt;pip&lt;/code&gt; from this container.  &lt;/p&gt;

&lt;p&gt;Here is some community code to do it:&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;LIB_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"python"&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LIB_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LIB_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;:/foo &lt;span class="nt"&gt;-w&lt;/span&gt; /foo lambci/lambda:build-python3.7 &lt;span class="se"&gt;\&lt;/span&gt;
    pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LIB_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  AWS CLI
&lt;/h4&gt;

&lt;p&gt;The aws cli v2 is in a docker container. No need to repeat the install instructions, please simply read them here at &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2-docker.html"&gt;Using the official AWS CLI version 2 Docker image&lt;/a&gt;.  The cli is faster than the console. &lt;br&gt;
It's simply a preference. &lt;/p&gt;

&lt;p&gt;Here's another community code example:&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; ~/.aws:/root/.aws &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;:/aws amazon/aws-cli s3 &lt;span class="nb"&gt;cp &lt;/span&gt;s3://aws-cli-docker-demo/hello &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



</description>
      <category>lambda</category>
      <category>python</category>
      <category>docker</category>
    </item>
    <item>
      <title>Async/Await with Nodejs File System</title>
      <dc:creator>starpebble</dc:creator>
      <pubDate>Sun, 07 Jun 2020 00:50:47 +0000</pubDate>
      <link>https://dev.to/starpebble/async-await-with-nodejs-file-system-1aa2</link>
      <guid>https://dev.to/starpebble/async-await-with-nodejs-file-system-1aa2</guid>
      <description>&lt;p&gt;It's still not possible to simply use memory for all our computing.  A single S3 &lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObject.html"&gt;object&lt;/a&gt; can grow up to 5TBs from a humble 1 byte. &lt;/p&gt;

&lt;h2&gt;
  
  
  fs Promises API
&lt;/h2&gt;

&lt;p&gt;Please let me show you how to simply read a tiny file in &lt;code&gt;/tmp&lt;/code&gt; named &lt;code&gt;data.json&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fsPromises&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;promises&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;data&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;fsPromises&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/tmp/data.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                     &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Failed to read file&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;require&lt;/code&gt; isn't exactly like Webpack's &lt;a href="https://webpack.js.org/guides/code-splitting/"&gt;code splitting&lt;/a&gt; with dynamic imports.  I promise it's static. It just kind of looks similar.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;readFile()&lt;/code&gt; returns a promise to &lt;code&gt;await&lt;/code&gt; upon in an &lt;code&gt;async&lt;/code&gt; function.  The single fulfilled promise provides one Node.js &lt;code&gt;Buffer&lt;/code&gt;.  This is a limitation.  The &lt;code&gt;Buffer&lt;/code&gt; must fit into the lambda's memory limit.  Keep a lambda safe and read small, in KBs.&lt;/p&gt;

&lt;h2&gt;
  
  
  thenable
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;f()&lt;/code&gt; is thenable.  Try and figure out error handling yourself.  This is just an example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
 &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
 &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;f() failed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;a href="https://nodejs.org/api/fs.html#fs_fspromises_readfile_path_options"&gt;readFile()&lt;/a&gt; is pretty simple.&lt;/p&gt;

&lt;h2&gt;
  
  
  File System (fs)
&lt;/h2&gt;

&lt;p&gt;A lambda can possibly write a file to a file system. Our use of a file system space is safe.  &lt;/p&gt;

&lt;p&gt;The Node.js File System Promises API is very nice. It's an extremely common module. &lt;code&gt;fs&lt;/code&gt; has an asynchronous API compatible with &lt;code&gt;await&lt;/code&gt;. It's somewhat exciting that Node.js is compatible with cloud bursting.  &lt;code&gt;fs&lt;/code&gt; knows how to read and write.&lt;/p&gt;

&lt;p&gt;Keep bursting! This is a sign I am comfortable holding up.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>asynchronous</category>
      <category>lambda</category>
    </item>
    <item>
      <title>How to explain in GraphQL-esque</title>
      <dc:creator>starpebble</dc:creator>
      <pubDate>Sun, 24 May 2020 17:49:05 +0000</pubDate>
      <link>https://dev.to/starpebble/how-to-explain-in-graphql-esque-382e</link>
      <guid>https://dev.to/starpebble/how-to-explain-in-graphql-esque-382e</guid>
      <description>&lt;p&gt;Explaining in GraphQL is easy.  Write single line comments with &lt;code&gt;#&lt;/code&gt;.  Offer more verbosity in multi-line descriptions with bracketing &lt;code&gt;"""&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Single Line Comment
&lt;/h3&gt;

&lt;p&gt;Start exactly with &lt;code&gt;#&lt;/code&gt; to comment a simple single line.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="c"&gt;# A snack may or may not be delicious.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Snack&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;delicious&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Perfectly safe in a GraphQL schema.&lt;/p&gt;

&lt;h3&gt;
  
  
  Multi-Line Description
&lt;/h3&gt;

&lt;p&gt;Bracket a multi-line comment with &lt;code&gt;"""&lt;/code&gt;.  GraphQL schema tools are smart.  Multi-line descriptions are useful to tools like &lt;a href="https://github.com/graphql/graphiql"&gt;Graphiql&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="s2"&gt;"""&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;A shirt.
It is a really simple type.&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;"""&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Shirt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;"""&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;shirt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;like&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;small&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;medium&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;depends&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;"""&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;threads&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;!,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;thickness&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Float&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Also safe in a GraphQL schema.&lt;/p&gt;

&lt;h3&gt;
  
  
  Query Comment
&lt;/h3&gt;

&lt;p&gt;A single line comment is safe in a query.  A multi-line description is not.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;snack&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="c"&gt;# this comment is safe&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Possible in a GraphQL query.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lack of comments is like a pandemic
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://graphql.org/"&gt;GraphQL&lt;/a&gt; is for people.  I don't disagree with code clarity as documentation.  Sometimes people who have nothing to do with code may read a GraphQL text block. It's shocking. Let's not forget that comments are fundamentally for our hyper connected readers. I don't know if there is a better way.&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>codequality</category>
      <category>writing</category>
    </item>
    <item>
      <title>Generating a QR code with React hooks and JavaScript</title>
      <dc:creator>starpebble</dc:creator>
      <pubDate>Mon, 04 May 2020 16:39:12 +0000</pubDate>
      <link>https://dev.to/starpebble/generating-a-qrcode-with-react-hooks-and-javascript-14l4</link>
      <guid>https://dev.to/starpebble/generating-a-qrcode-with-react-hooks-and-javascript-14l4</guid>
      <description>&lt;p&gt;A single react function component can place an image like a QR code in a HTML5 canvas tag.&lt;/p&gt;

&lt;h3&gt;
  
  
  Screenshot
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UBhG0MpR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/oqjb3wtfagk6kmsv5tc1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UBhG0MpR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/oqjb3wtfagk6kmsv5tc1.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  QRCode function component example
&lt;/h3&gt;

&lt;p&gt;Here's how to do this without Kubernetes.  This example is just JavaScript. &lt;/p&gt;

&lt;p&gt;Example JavaScript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;QRCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;size&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;canvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;useEffect&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;qr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;QRious&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;element&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/canvas&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Props
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;QRCode&lt;/code&gt; is a &lt;a href="https://reactjs.org/docs/components-and-props.html"&gt;function component&lt;/a&gt;.  &lt;code&gt;QRCode&lt;/code&gt; accepts two props. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;text&lt;/li&gt;
&lt;li&gt;size
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The generated QR code image encodes the text. Size controls the image size.&lt;/p&gt;

&lt;h3&gt;
  
  
  React hooks in this function component
&lt;/h3&gt;

&lt;p&gt;The function component uses exactly two hooks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;useRef&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;useEffect&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;useRef()&lt;/code&gt; is an essential react hook.  It's almost impossible for a React function component to draw on an HTML5 &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; without this hook.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;useEffect()&lt;/code&gt; helps the function component by listening to React component lifecycle events.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dependencies
&lt;/h3&gt;

&lt;p&gt;Include the cdn hosted versions as &lt;code&gt;&amp;lt;script/&amp;gt;&lt;/code&gt; tags:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://cdnjs.com/libraries/react"&gt;React&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cdnjs.com/libraries/react-dom"&gt;ReactDOM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cdnjs.com/libraries/qrious"&gt;QRious&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://unpkg.com/"&gt;unpkg&lt;/a&gt; is reliable. I love it. &lt;a href="https://cdnjs.com"&gt;cdnjs&lt;/a&gt; is unstoppable! I love it too. Thanks &lt;a href="https://www.cloudflare.com/"&gt;Cloudflare&lt;/a&gt;!&lt;/p&gt;

&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;

&lt;p&gt;A single html file example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;QRCode with React Hooks and JS&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"description"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"QRCode with React Hooks and JS."&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"keywords"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"JavaScript, React"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;    
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"minimum-scale=1, initial-scale=1, width=device-width"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://unpkg.com/react@latest/umd/react.development.js"&lt;/span&gt; &lt;span class="na"&gt;crossorigin=&lt;/span&gt;&lt;span class="s"&gt;"anonymous"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://unpkg.com/react-dom@latest/umd/react-dom.development.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://unpkg.com/babel-standalone@latest/babel.min.js"&lt;/span&gt; &lt;span class="na"&gt;crossorigin=&lt;/span&gt;&lt;span class="s"&gt;"anonymous"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://unpkg.com/qrious@latest/dist/qrious.min.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt; 
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"root"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/babel"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="c1"&gt;// React Hooks&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;useEffect&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// QRCode&lt;/span&gt;
&lt;span class="c1"&gt;//&lt;/span&gt;
&lt;span class="c1"&gt;// Function component, creates one QR code image    &lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;QRCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;size&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;canvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;useEffect&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;qr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;QRious&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;element&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/canvas&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// App&lt;/span&gt;
&lt;span class="c1"&gt;//&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="o"&gt;=&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;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://dev.to&lt;/span&gt;&lt;span class="dl"&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;align&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;One&lt;/span&gt; &lt;span class="nx"&gt;QRCode&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;QRCode&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;100&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&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;text&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&amp;gt;    &lt;/span&gt;&lt;span class="err"&gt; 
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;ReactDOM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#root&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="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Just one way
&lt;/h3&gt;

&lt;p&gt;There are dozens of ways to generate a single QR code image.  I'm writing down a list.  Please feel free to share the best libraries as a comment.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>security</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Integrating a REST API into an Amplify app's GraphQL model</title>
      <dc:creator>starpebble</dc:creator>
      <pubDate>Thu, 09 Apr 2020 03:28:33 +0000</pubDate>
      <link>https://dev.to/starpebble/integrating-a-rest-api-into-an-amplify-app-s-graphql-model-22ai</link>
      <guid>https://dev.to/starpebble/integrating-a-rest-api-into-an-amplify-app-s-graphql-model-22ai</guid>
      <description>&lt;p&gt;Consolidate a REST API call into a GraphQL model with an &lt;a href="https://aws.amazon.com/appsync/"&gt;AppSync&lt;/a&gt; HTTP resolver.  Here are some instructions.  Don't forget to read the summary no less than five times.&lt;/p&gt;

&lt;h2&gt;
  
  
  Weather Example
&lt;/h2&gt;

&lt;p&gt;What is the weather in Anchorage, Alaska? Let's ask &lt;a href="https://openweathermap.org/current#min"&gt;OpenWeatherMap.org&lt;/a&gt; in a new way with &lt;a href="https://graphql.org/"&gt;GraphQL&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# schema.graphql
type Weather {
  zipCode: String!
  temperature: String
}

type Query {
  # getWeatherByZipCode is resolved by https://api.openweathermap.org/data/2.5/weather
  getWeatherByZipCode(zipCode: String!) : Weather
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="c"&gt;# example graphql client query&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;GetWeather&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;getWeatherByZipCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;zipCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"99504"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;zipCode&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"getWeatherByZipCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"zipCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"99504"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"temperature"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"274.48"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;It's cold in Anchorage, Alaska.  OpenWeatherMap.org reports the temperature in Kelvin. 274.48 K degrees is chilly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Instructions
&lt;/h2&gt;

&lt;p&gt;Let's simply add exactly one AppSync &lt;a href="https://docs.aws.amazon.com/appsync/latest/devguide/tutorial-http-resolvers.html"&gt;HTTP resolver&lt;/a&gt; to an &lt;a href="https://aws-amplify.github.io/"&gt;Amplify&lt;/a&gt; app.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add one GraphQL API with &lt;code&gt;amplify add api&lt;/code&gt; and name it however you like&lt;/li&gt;
&lt;li&gt;Add one &lt;code&gt;query getWeatherByZipCode&lt;/code&gt; and one &lt;code&gt;type Weather&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Add one request &lt;a href="http://velocity.apache.org/engine/2.0/vtl-reference.html"&gt;VTL&lt;/a&gt; for the resolver&lt;/li&gt;
&lt;li&gt;Add one response &lt;a href="http://velocity.apache.org/engine/2.0/vtl-reference.html"&gt;VTL&lt;/a&gt; for the resolver
&lt;/li&gt;
&lt;li&gt;Add one CloudFormation &lt;code&gt;AWS::AppSync::DataSource&lt;/code&gt; resource&lt;/li&gt;
&lt;li&gt;Add one CloudFormation &lt;code&gt;AWS::AppSync::Resolver&lt;/code&gt; resource&lt;/li&gt;
&lt;li&gt;Push to the cloud with &lt;code&gt;amplify push&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;1. Add one GraphQL API&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;amplify add api
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Add one request VTL for the query getWeatherByZipCode&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Add one request VTL in a file named &lt;code&gt;Query.getWeatherByZipCode.req.vtl&lt;/code&gt; in the resolvers directory &lt;code&gt;amplify/backend/api/&amp;lt;apiname&amp;gt;/resolvers&lt;/code&gt;.  The VTL file will invoke the openweather.org REST API when the amplify app asks AppSync for the weather.  &lt;/p&gt;

&lt;p&gt;The request VTL template programs AppSync to get the weather from OpenWeatherMap.org with an HTTP GET.&lt;/p&gt;

&lt;p&gt;Contents of &lt;code&gt;Query.getWeatherByZipCode.req.vtl&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#set( $appid = $util.defaultIfNull($context.args.appid, "&amp;lt;secretopenweathermapapikey&amp;gt;") )
{
    "version": "2018-05-29",
    "method": "GET",
    "params" : {
        "headers" : {
            "Content-Type": "application/json"
        }
    },
    "resourcePath": $util.toJson("/data/2.5/weather?zip=${ctx.args.zipCode}&amp;amp;appid=${appid}")
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Add one &lt;code&gt;query getWeatherByZipCode&lt;/code&gt; and one &lt;code&gt;type Weather&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Weather&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;zipCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;getWeatherByZipCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;zipCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Weather&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Add one response VTL for the query getWeatherByZipCode&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Add one response VTL in a file named &lt;code&gt;Query.getWeatherByZipCode.res.vtl&lt;/code&gt; in the resolvers directory &lt;code&gt;amplify/backend/api/&amp;lt;apiname&amp;gt;/resolvers&lt;/code&gt;.  &lt;/p&gt;

&lt;p&gt;The response VTL programs AppSync to transform the OpenWeatherMap.org &lt;a href="https://samples.openweathermap.org/data/2.5/weather?zip=94040,us&amp;amp;appid=b6907d289e10d714a6e88b30761fae22"&gt;REST API response&lt;/a&gt; into the Weather type in our GraphQL schema.&lt;/p&gt;

&lt;p&gt;Contents of &lt;code&gt;Query.getWeatherByZipCode.res.vtl&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;## Raise a GraphQL field error in case of a datasource invocation error
#if($ctx.error)
  $util.error($ctx.error.message, $ctx.error.type)
#end
#if($ctx.result.statusCode == 200)
## If response is 200, return the body.
  ## The response is a JSON string.  Let us parse it.
  #set( $json = $util.parseJson($ctx.result.body))
  ## Cherry pick the temperature field
  $util.toJson({
    "zipCode": $ctx.args.zipCode,
    "temperature": $json.main.temp
  })    
#else
  ## If response is not 200, append error message to response
  $utils.appendError($ctx.result.body, "$ctx.result.statusCode")
#end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;5. Add one CloudFormation &lt;code&gt;AWS::AppSync::DataSource&lt;/code&gt; resource&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Add one &lt;code&gt;AWS::AppSync::DataSource&lt;/code&gt; resource to the CustomResources.json template in &lt;code&gt;amplify/backend/api/&amp;lt;apiname&amp;gt;/stack/CustomResources.json&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"WeatherDataSource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AWS::AppSync::DataSource"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ApiId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Ref"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AppSyncApiId"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Weather"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"HTTP"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"HttpConfig"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"Endpoint"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://api.openweathermap.org"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;6. Add one CloudFormation &lt;code&gt;AWS::AppSync::Resolver&lt;/code&gt; resource&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Add one &lt;code&gt;AWS::AppSync::Resolver&lt;/code&gt; resource to the CustomResources.json template in &lt;code&gt;amplify/backend/api/&amp;lt;apiname&amp;gt;/stack/CustomResources.json&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The resource programs AppSync to ask the &lt;code&gt;WeatherDataSource&lt;/code&gt; for weather when a client invokes the &lt;code&gt;getWeatherByZipCode(zipCode: String!)&lt;/code&gt; query. &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;FieldName&lt;/code&gt; property is like glue.  That's kinda why CloudFormation is like a glue gun.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"QueryOpenWeatherResolver": {
  "Type": "AWS::AppSync::Resolver",
  "DependsOn" : "WeatherDataSource",
  "Properties": {
    "ApiId": {
      "Ref": "AppSyncApiId"
    },
    "DataSourceName": "Weather",
    "TypeName": "Query",
    "FieldName": "getWeatherByZipCode",
    "RequestMappingTemplateS3Location": {
      "Fn::Sub": [
        "s3://${S3DeploymentBucket}/${S3DeploymentRootKey}/resolvers/Query.getWeatherByZipCode.req.vtl",
        {
          "S3DeploymentBucket": {
            "Ref": "S3DeploymentBucket"
          },
          "S3DeploymentRootKey": {
            "Ref": "S3DeploymentRootKey"
          }
        }
      ]
    },
    "ResponseMappingTemplateS3Location": {
      "Fn::Sub": [
        "s3://${S3DeploymentBucket}/${S3DeploymentRootKey}/resolvers/Query.getWeatherByZipCode.res.vtl",
        {
          "S3DeploymentBucket": {
            "Ref": "S3DeploymentBucket"
          },
          "S3DeploymentRootKey": {
            "Ref": "S3DeploymentRootKey"
          }
        }
      ]
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;7. Push to the cloud with &lt;code&gt;amplify push&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;amplify push
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Simplified Thinking
&lt;/h3&gt;

&lt;p&gt;It's like wrangling data sources.  Herd sources into exactly one spot to reduce complexity of client side code. Benefit from an increasingly consistent mental model of how an app gets data from the world.  When the weather gets popular, I can simply turn on AppSync caching and make sure I'm not slowing the world down.  &lt;/p&gt;

&lt;h3&gt;
  
  
  @http directive, throw my hands up
&lt;/h3&gt;

&lt;p&gt;Just thinking out loud, maybe &lt;code&gt;@http&lt;/code&gt; should simply be a directive on a field, like &lt;code&gt;@function&lt;/code&gt;. &lt;code&gt;@http&lt;/code&gt; would make a REST call where &lt;code&gt;@function&lt;/code&gt; invokes a lambda. Amplify supports &lt;a href="https://aws-amplify.github.io/docs/cli-toolchain/graphql#data-access-patterns"&gt;Lambda custom resolvers&lt;/a&gt; and omits HTTP resolvers.  Don't let that stop you!  It's a new way for an app to access a web service. &lt;/p&gt;

&lt;p&gt;There are other ways to simplify.  What's the best? If there is a better way to simplify, please leave a comment below.&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>amplify</category>
      <category>appsync</category>
      <category>aws</category>
    </item>
    <item>
      <title>Versioning git pushes of React websites </title>
      <dc:creator>starpebble</dc:creator>
      <pubDate>Fri, 03 Apr 2020 01:32:14 +0000</pubDate>
      <link>https://dev.to/starpebble/versioning-git-pushes-of-react-websites-2h9b</link>
      <guid>https://dev.to/starpebble/versioning-git-pushes-of-react-websites-2h9b</guid>
      <description>&lt;p&gt;Create a web page with an auto-incremented version number. Let git and npm automate it. Perfect for a small project with a single developer. It's the &lt;a href="https://docs.npmjs.com/files/package.json"&gt;package.json&lt;/a&gt; approach with auto-bump.&lt;/p&gt;

&lt;h3&gt;
  
  
  Auto-Increment the package.json version number
&lt;/h3&gt;

&lt;p&gt;Here's how to auto-increment a version number in package.json with git, npm, and react. Three steps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1&lt;/strong&gt;: Create one &lt;a href="https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks"&gt;git hook&lt;/a&gt; for the &lt;code&gt;pre-push&lt;/code&gt; git command.  It's just a text file. Create one file named 'pre-push' in the directory .git/hooks.&lt;/p&gt;

&lt;p&gt;Example file structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;tree .git/hooks
.git/hooks
├── applypatch-msg.sample
├── commit-msg.sample
├── post-update.sample
├── pre-applypatch.sample
├── pre-commit.sample
├── prepare-commit-msg.sample
├── pre-push
├── pre-push.sample
├── pre-rebase.sample
├── pre-receive.sample
└── update.sample
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2&lt;/strong&gt;: Add the executable file permission. On linux, with &lt;code&gt;chmod&lt;/code&gt;.   &lt;/p&gt;

&lt;p&gt;Example command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt; &lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x .git/hooks/pre-push
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3&lt;/strong&gt;: Place the following two lines into the file 'pre-push':&lt;/p&gt;

&lt;p&gt;Example hook content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npm version patch
&lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Done!&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Trigger the auto-increment
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://git-scm.com/docs/git-push"&gt;&lt;code&gt;git push&lt;/code&gt;&lt;/a&gt; triggers the auto-increment. git will execute the hook first. The hook is the shell script above. git then executes the push command to the upstream git repository. &lt;code&gt;[npm version](https://docs.npmjs.com/cli/version)&lt;/code&gt; increments the version number and commits the package.json to the git repository. &lt;code&gt;npm version&lt;/code&gt; shoves the new version number into the commit message. A one-liner.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm version&lt;/code&gt; tags the local git repository. npm is nifty. The tag is a version number and it matches the package.json's 'version' value. Whether the tag is pushed to the upstream repository depends on the 'git push' command line arguments. By default, git does not push tags upstream.&lt;/p&gt;

&lt;h3&gt;
  
  
  Give the version number to the users
&lt;/h3&gt;

&lt;p&gt;Place the version number in a react component.  Then, users will see the exact version number!  &lt;/p&gt;

&lt;p&gt;Example react component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&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;react&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;version&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;../../../package.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// about page&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;About&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;render&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;(&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Thanks to all my friends!&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;v&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The package.json file is a private file with important information.  Don't import the whole file.  Scope the import to one key/value pair.  It's safe.&lt;/p&gt;

&lt;h3&gt;
  
  
  Versioning website content
&lt;/h3&gt;

&lt;p&gt;Website content is kinda like code.  A version number is like a sync point. A small project can benefit too from one that is auto-bumped. That's why a teeny tiny automated commit hook is perfect for a web site with a single developer. &lt;/p&gt;

&lt;h3&gt;
  
  
  'version' in package.json
&lt;/h3&gt;

&lt;p&gt;The package.json is a simple place to keep the version number. So let's use that file. '&lt;a href="https://docs.npmjs.com/cli/version"&gt;npm version&lt;/a&gt;' can bump it. JavaScript can simply &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules"&gt;import&lt;/a&gt; it. JavaScript can import a single 'version' key/value pair from the json file in the website content and exclude the rest of the file.  &lt;/p&gt;

&lt;p&gt;Example package.json:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"scotch"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.1.3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"private"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"react"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^16.12.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"react-dom"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^16.12.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"react-scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3.2.0"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"react-scripts start"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"eslintConfig"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"extends"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"react-app"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Version numbers are not cache busters
&lt;/h3&gt;

&lt;p&gt;I've never really liked cache busters.  Cache invalidation is simpler.  An amazing number of website bugs root from web browser cache staleness.  A version number isn't a cache buster.  It simply a sync point.  git and npm are beautiful tools and I really like them.  That's why I let these two tools handle my version number increments for my small website projects. Is is the simplest? Please share any other simple solutions as a comment!&lt;/p&gt;

</description>
      <category>npm</category>
      <category>react</category>
      <category>git</category>
      <category>devops</category>
    </item>
    <item>
      <title>Quickly populating a second AWS Amplify GraphQL environment with data</title>
      <dc:creator>starpebble</dc:creator>
      <pubDate>Tue, 17 Mar 2020 16:11:31 +0000</pubDate>
      <link>https://dev.to/starpebble/quickly-populating-a-second-aws-amplify-graphql-environment-with-data-5fa8</link>
      <guid>https://dev.to/starpebble/quickly-populating-a-second-aws-amplify-graphql-environment-with-data-5fa8</guid>
      <description>&lt;p&gt;Sometimes it's more comfortable to test an AWS Amplify app with active data. '&lt;a href="https://aws-amplify.github.io/docs/cli-toolchain/quickstart?sdk=js#environment-related-commands"&gt;amplify env add&lt;/a&gt;' creates emptiness. The GraphQL endpoint will return nothingness. Fill it up! Maybe replicate data from production, or maybe bootstrap from files in S3.&lt;/p&gt;

&lt;h3&gt;
  
  
  Approaches
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Bootstrap from Files&lt;/strong&gt;&lt;br&gt;
Quickly upload data the second environment from data in files. To DynamoDB or RDS. Simply put data files in S3 and replicate with AWS Data Migration Service. An empty environment can be painful. Data with velocity can be painful too. Static files are really simple and can take the pain away:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS Data Migration Service (for DynamoDB or RDS resolved models)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Replicate from Production&lt;/strong&gt;&lt;br&gt;
Quickly populate a second environment with data from the production environment for comfortable testing. How exactly can somebody replicate production data to a second environment? It's easy. There are two services which can replicate the production app environment's data stores:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS Data Pipeline (for DynamoDB resolved models)&lt;/li&gt;
&lt;li&gt;AWS Data Migration Service (for RDS resolved models)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Bootstrap from Files
&lt;/h3&gt;

&lt;p&gt;AWS Data Migration Service can copy data from S3.  DMS can copy to DynamoDB or RDS. Create a source endpoint in AWS DMS with a single S3 bucket.  See &lt;a href="https://docs.aws.amazon.com/dms/latest/userguide/CHAP_Source.S3.html"&gt;Using Amazon S3 as a Source for AWS DMS&lt;/a&gt;. Then create a destination endpoint, for example, a single AWS DynamoDB endpoint. See &lt;a href="https://docs.aws.amazon.com/dms/latest/userguide/CHAP_Target.DynamoDB.html"&gt;Using an Amazon DynamoDB Database as a Target for AWS Database Migration Service&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Replicate from Production
&lt;/h3&gt;

&lt;p&gt;Choose a service to replicate a particular type of data store.  AWS Data Pipeline can perfectly copy one DynamoDB table to another.  AWS Data Migration Service can perfectly copy one RDS hosted database instance to another.&lt;/p&gt;

&lt;p&gt;Jump in the saddle with AWS Data Pipeline to replicate a DynamoDB single table. Expect a quick hop to S3.  See &lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBPipeline.html"&gt;Exporting and Importing DynamoDB data with AWS Data Pipeline&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Replicating a single RDS hosted database from one instance to another is simple. Use AWS Data Migration Service to copy the tables over. It won't take months. It will be fast.&lt;/p&gt;

&lt;h3&gt;
  
  
  Possibilities
&lt;/h3&gt;

&lt;p&gt;When it's absolutely necessary to have data in an Amplify environment, don't let it be cumbersome. A GraphQL endpoint is more functional with data. Iterative software feature delivery goes faster. Push the data replication into the cloud! Have fun. There might be more than one way.  Leave a comment with other suggestions, please!&lt;/p&gt;

</description>
      <category>amplify</category>
      <category>devops</category>
      <category>database</category>
      <category>testing</category>
    </item>
    <item>
      <title>Hosting The Hugo QuickStart Project on AWS Cloudfront with Lambda@Edge</title>
      <dc:creator>starpebble</dc:creator>
      <pubDate>Mon, 09 Mar 2020 17:34:51 +0000</pubDate>
      <link>https://dev.to/starpebble/hosting-the-hugo-quickstart-project-on-aws-cloudfront-with-lambda-edge-5g5f</link>
      <guid>https://dev.to/starpebble/hosting-the-hugo-quickstart-project-on-aws-cloudfront-with-lambda-edge-5g5f</guid>
      <description>&lt;p&gt;The Hugo &lt;a href="https://gohugo.io/getting-started/quick-start/"&gt;QuickStart&lt;/a&gt; is awfully simple.  Why not host the quickstart project with serverless? Here is a guide to hosting Hugo content on serverless. The guide describes the steps to host the Hugo QuickStart project on Cloudfront with &lt;a href="mailto:Lambda@Edge"&gt;Lambda@Edge&lt;/a&gt;. It's really simplified. For fun, the code is nicknamed Flavor Cafe (Scotch). Go further than &lt;a href="https://gohugo.io/hosting-and-deployment/hugo-deploy/"&gt;hugo deploy&lt;/a&gt;, which stops at S3.  &lt;/p&gt;

&lt;h3&gt;
  
  
  Flavor Cafe (Scotch) - Hugo on Serverless
&lt;/h3&gt;

&lt;p&gt;I've hosted the example quickstart site on serverless, visit the site at &lt;a href="https://scotch.spicykey.com/"&gt;https://scotch.spicykey.com/&lt;/a&gt;. The site is securely served with https (http secure). Visit for fun.&lt;/p&gt;

&lt;p&gt;Use the Cloudformation template in this post to try it. Any serverless computing enthusiast can use the template in his or her AWS account.  The lambda@edge JavaScript function is teeny tiny and won't take up too much space in the cloud.&lt;/p&gt;

&lt;h3&gt;
  
  
  How it Works
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Hosting the uri '/posts/my-first-post/'&lt;/em&gt;&lt;br&gt;
The Hugo web server handles a gotcha - uri rewriting. That's why the quickstart project generated link '&lt;a href="https://scotch.spicykey.com/posts/my-first-post/"&gt;/posts/my-first-post/&lt;/a&gt;' is served with content from '&lt;a href="https://scotch.spicykey.com/posts/my-first-post/index.html"&gt;/posts/my-first-post/index.html&lt;/a&gt;' by the Hugo server. '/posts/my-first-post/' is a sub directory distinguished by a trailing '/'.  Cloudfront can also handle this sub directory gotcha. Cloudfront simply needs a little customization with &lt;a href="mailto:Lambda@Edge"&gt;Lambda@Edge&lt;/a&gt;. We can control Cloudfront's behavior where a Cloudfront distribution will accept rewritten uri's from a Lambda@Edge function. The lambda function will rewrite the uri's for sub directories. That means the quickstart project site will be easy to click around when hosted on Cloudfront.  A web browser request for '/posts/my-first-post/' will be successful.  The web browser will receive the content of the static file '/posts/my-first-post/index.html' hosted on S3.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Success&lt;/em&gt;&lt;br&gt;
Flavor Cafe (Scotch) changes how Cloudfront behaves.  This is a screenshot of the link '&lt;a href="https://scotch.spicykey.com/posts/my-first-post/"&gt;/posts/my-first-post/&lt;/a&gt;'&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sGjOp8U---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/5kxxvj0d2mbp3mk7a7ru.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sGjOp8U---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/5kxxvj0d2mbp3mk7a7ru.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Failure&lt;/em&gt;&lt;br&gt;
When Cloudfront receives a sub directory request such as '/posts/my-first-post/', it can fail.  This is what failure looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;This XML file does not appear to have any style information associated with it. The document tree is shown below.
&amp;lt;Error&amp;gt;
&amp;lt;Code&amp;gt;AccessDenied&amp;lt;/Code&amp;gt;
&amp;lt;Message&amp;gt;Access Denied&amp;lt;/Message&amp;gt;
&amp;lt;RequestId&amp;gt;EA44DB04106A1902&amp;lt;/RequestId&amp;gt;
&amp;lt;HostId&amp;gt;
ZslQyfgBnRJ/XPC2mVpNk8k/EBhCk+zE9Qa4zJ5pJIFwgeGKqekH0pF+gJoeQwrjPkD4uHGsFG4=
&amp;lt;/HostId&amp;gt;
&amp;lt;/Error&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Lambda
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Flavor Cafe (Scotch) Lambda@Edge Code&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;'use strict';

// @starpebble on github
// hugo flavor cafe (scotch)

const DEFAULT_OBJECT = 'index.html';

exports.handler = (event, context, callback) =&amp;gt; {
  const cfrequest = event.Records[0].cf.request;
  if (cfrequest.uri.length &amp;gt; 0 &amp;amp;&amp;amp; cfrequest.uri.charAt(cfrequest.uri.length - 1) === '/') {
    // e.g. /posts/ to /posts/index.html
    cfrequest.uri += DEFAULT_OBJECT;
  }
  else if (!cfrequest.uri.match(/.(css|md|gif|ico|jpg|jpeg|js|png|txt|svg|woff|ttf|map|json|html)$/)) {
    // e.g. /posts to /posts/index.html
    cfrequest.uri += `/${DEFAULT_OBJECT}`;
  }
  callback(null, cfrequest);
  return true;
};     
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;em&gt;JavaScript Code Comments&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Hugo on Cloudfront, Lambda@Edge function 
// Flavor Cafe (Scotch)
// @starpebble on github
//
// Two rewrite rules for hugo sub directory uri's.
// Example:
//   1. rewrite uri /posts/ to /posts/index.html
//   2. rewrite uri /posts  to /posts/index.html
//
// Add as many file extensions as you like for rule 2.
// uri's that end in a known file extensions are not rewritten by rule 2.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;em&gt;200 not 404&lt;/em&gt;&lt;br&gt;
The &lt;a href="https://aws.amazon.com/lambda/edge/"&gt;Lambda@Edge&lt;/a&gt; function takes will rewrite the Hugo QuickStart project urls for directories to a default object, index.html.  That's how Cloudfront serves the URI '/posts/my-first-post/' with content '/posts/my-first-posts/index.html' returning a perfect 200 instead of an ugly 404.&lt;/p&gt;

&lt;p&gt;Trust me, don't try to host a Hugo site on Cloudfront without a little customization with Lambda! Here's how we can make it easy for user's to click around a Hugo site hosted on Cloudfront.&lt;/p&gt;

&lt;p&gt;Cloudfront is an amazing machine.  When content does fit perfectly into its rules, Cloudfront can be customized with &lt;a href="mailto:lambda@edge"&gt;lambda@edge&lt;/a&gt;. Each request for content is simply an event to a lambda function replicated to the edge, just like the static html content generated by Hugo.  That means the lambda is replicated to points of presence all around the world, in proximity to users.&lt;/p&gt;

&lt;p&gt;The Lambda@Edge nodejs function is very small.  This is important.  Smaller lambda functions are better, with size measured in bytes.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Steps&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Launch the Flavor Cafe (Scotch) Cloudformation template in this post, once&lt;/li&gt;
&lt;li&gt;Write down the Cloudfront domain url, one output of the template&lt;/li&gt;
&lt;li&gt;Create one quickstart project with Hugo &lt;a href="https://gohugo.io/getting-started/quick-start/"&gt;guide&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Modify the config.toml baseURL key with the Cloudfront domain url and secure https protocol string, e.g. 'baseURL = "&lt;a href="https://d123456789.cloudfront.net%22"&gt;https://d123456789.cloudfront.net"&lt;/a&gt;'&lt;/li&gt;
&lt;li&gt;hugo -D&lt;/li&gt;
&lt;li&gt;Write down the S3 bucket name, one output of the template&lt;/li&gt;
&lt;li&gt;Upload the hugo generated content in the directory 'public/' to the S3 bucket, such as 'aws s3 sync'&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Template
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Cloudformation YAML&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AWSTemplateFormatVersion: 2010-09-09
Description: 'Flavor Cafe (Scotch), host a static Hugo site on Cloudfront'
# one cloudfront distribution with one s3 bucket origin
# one lambda@edge function rewrites cloudfront sub directory requests to a root object
Parameters:
  flavor:
    Type: String
    Description: name of flavor
    Default: 'scotch'
Resources:
  LambdaEdgeFunctionRole:
    Type: "AWS::IAM::Role"
    Properties:
        Path: "/"
        ManagedPolicyArns:
            - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
        AssumeRolePolicyDocument:
          Version: "2012-10-17"
          Statement:
            - Sid: "AllowLambdaServiceToAssumeRole"
              Effect: "Allow"
              Action:
                - "sts:AssumeRole"
              Principal:
                Service:
                  - "lambda.amazonaws.com"
                  - "edgelambda.amazonaws.com"
  LambdaAtEdgeFunction:
    DependsOn: 
      - LambdaEdgeFunctionRole
    Type: AWS::Lambda::Function
    Properties:
      Description: !Sub 'flavorcafe ${flavor} jamstack hugo lambda@edge'
      FunctionName: !Sub hugo-flavorcafe-${flavor}-function
      Role: !GetAtt LambdaEdgeFunctionRole.Arn
      Handler: index.handler
      Runtime: nodejs10.x
      Timeout: 1
      MemorySize: 128
      Code:
        ZipFile: &amp;gt;
          'use strict';

          // @starpebble on github
          // hugo flavor cafe (scotch)

          const DEFAULT_OBJECT = 'index.html';

          exports.handler = (event, context, callback) =&amp;gt; {
            const cfrequest = event.Records[0].cf.request;
            if (cfrequest.uri.length &amp;gt; 0 &amp;amp;&amp;amp; cfrequest.uri.charAt(cfrequest.uri.length - 1) === '/') {
              // e.g. /posts/ to /posts/index.html
              cfrequest.uri += DEFAULT_OBJECT;
            }
            else if (!cfrequest.uri.match(/.(css|md|gif|ico|jpg|jpeg|js|png|txt|svg|woff|ttf|map|json|html)$/)) {
              // e.g. /posts to /posts/index.html
              cfrequest.uri += `/${DEFAULT_OBJECT}`;
            }
            callback(null, cfrequest);
            return true;
          };          

  LambdaFunctionVersion:
    DependsOn:
      - LambdaAtEdgeFunction
    Type: AWS::Lambda::Version
    Properties: 
      Description: 'Lambda@Edge version'
      FunctionName: !Ref LambdaAtEdgeFunction
  S3Bucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub hugo-flavorcafe-${flavor}-${AWS::AccountId}
      BucketEncryption: 
        ServerSideEncryptionConfiguration: 
        - ServerSideEncryptionByDefault:
            SSEAlgorithm: AES256
      AccessControl: Private
      WebsiteConfiguration:
        IndexDocument: index.html
        ErrorDocument: error.html
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        IgnorePublicAcls: true
        BlockPublicPolicy: true
        RestrictPublicBuckets: true
      CorsConfiguration:
        CorsRules:
          -
            AllowedOrigins:
              - 'http*'
            AllowedMethods:
              - HEAD
              - GET
              - PUT
              - POST
              - DELETE
            AllowedHeaders:
              - '*'
            ExposedHeaders:
              - ETag
              - x-amz-meta-custom-header
  CFOriginAccessIdentity:
    Type: AWS::CloudFront::CloudFrontOriginAccessIdentity
    Properties:
      CloudFrontOriginAccessIdentityConfig:
        Comment: !Sub FlavorCafe ${flavor} CloudFrontOAI
  S3BucketPolicy:
    DependsOn:
      - S3Bucket
      - CFOriginAccessIdentity
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref S3Bucket
      PolicyDocument:
        Statement:
          -
            Effect: Allow
            Action: s3:GetObject
            Principal:
              CanonicalUser: !GetAtt CFOriginAccessIdentity.S3CanonicalUserId
            Resource: !Sub 'arn:aws:s3:::${S3Bucket}/*'  
  CFDistribution:
    DependsOn:
      - LambdaFunctionVersion
      - S3Bucket
      - CFOriginAccessIdentity
    Type: AWS::CloudFront::Distribution
    Properties:
      DistributionConfig:
        Enabled: 'true'
        HttpVersion: 'http2'
        Comment: 'hugo flavor cafe distribution'
        DefaultRootObject: index.html
        Origins:
        - Id: S3OriginPrivateContent
          DomainName: !Sub hugo-flavorcafe-${flavor}-${AWS::AccountId}.s3.amazonaws.com
          S3OriginConfig:
            OriginAccessIdentity: !Sub origin-access-identity/cloudfront/${CFOriginAccessIdentity}
        DefaultCacheBehavior:
          TargetOriginId: S3OriginPrivateContent
          Compress: true
          ForwardedValues:
            QueryString: 'false'
            Headers:
              - Origin
            Cookies:
              Forward: none
          ViewerProtocolPolicy: redirect-to-https
          LambdaFunctionAssociations:
            - EventType: viewer-request
              LambdaFunctionARN: !Ref LambdaFunctionVersion
Outputs:
  BucketName:
    Description: The hugo content bucket name
    Value: !Ref S3Bucket
  CloudfrontDomain:
    Description: the cloudfront hosted dns domain name for the hugo static site
    Value: !GetAtt CFDistribution.DomainName
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Diagrams
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Serverless Resources&lt;/em&gt;&lt;br&gt;
A picture is worth a thousand words. Here are two diagrams of the template above. The template can host a Hugo site like &lt;a href="https://scotch.spicykey.com/"&gt;scotch.spicykey.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1Z9hZwb_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/lb764w4s97r5zbj2vucs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1Z9hZwb_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/lb764w4s97r5zbj2vucs.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eS6oCqKV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/l8ouyn1ni88tvwxcdemc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eS6oCqKV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/l8ouyn1ni88tvwxcdemc.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Keep going
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Just for fun&lt;/em&gt;&lt;br&gt;
Hugo is fun.  Go further than S3 and try Cloudfront. &lt;a href="https://gohugo.io/hosting-and-deployment/hugo-deploy/"&gt;Hugo deploy&lt;/a&gt; is really different because it hosts the site on S3. Cloudfront is speedy, with http/2 and content hosted at the edge of the cloud. Have fun with the amazing content machine, Cloudfront! &lt;/p&gt;

&lt;p&gt;starpebble&lt;br&gt;
&lt;a href="https://github.com/starpebble"&gt;https://github.com/starpebble&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>hugo</category>
      <category>cloudfront</category>
      <category>serverless</category>
    </item>
    <item>
      <title>Identifying the OS version and phone model hosting a React Native mobile app purely with JavaScript</title>
      <dc:creator>starpebble</dc:creator>
      <pubDate>Mon, 03 Feb 2020 05:08:19 +0000</pubDate>
      <link>https://dev.to/starpebble/identifying-the-os-version-and-phone-model-hosting-a-react-native-mobile-app-purely-with-javascript-4j92</link>
      <guid>https://dev.to/starpebble/identifying-the-os-version-and-phone-model-hosting-a-react-native-mobile-app-purely-with-javascript-4j92</guid>
      <description>&lt;p&gt;When it is absolutely necessary to determine the OS and phone model in a &lt;a href="https://facebook.github.io/react-native/"&gt;React Native&lt;/a&gt; app it can be done with purely with JavaScript.  Please let me explain how exactly the open source React Native component &lt;a href="https://github.com/starpebble/info-glass"&gt;info-glass&lt;/a&gt; does it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;User Agent String&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The React Native &lt;a href="https://facebook.github.io/react-native/docs/webview.html"&gt;WebView&lt;/a&gt; component can execute a simple JavaScript function that returns the user agent string.  That means the WebView will kinda reply like a web browser does.  &lt;/p&gt;

&lt;p&gt;The user agent string identifies the mobile app's host:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;OS name&lt;/li&gt;
&lt;li&gt;OS version&lt;/li&gt;
&lt;li&gt;Phone model&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It doesn't have to be a phone the WebView can also identify a mobile device model like a tablet too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script&amp;gt;
  function getinfo() {
    if(window.postMessage.length !== 1) {
      setTimeout(getinfo, ${ASYNCHRONOUS_DELAY_MS});
    } else {
      window.postMessage(window.navigator.userAgent);
    }
  }
  window.onload = getinfo;
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The code snippet above is just a single javascript function named getinfo() in a single HTML script tag. The function getinfo() returns the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/NavigatorID/userAgent"&gt;window.navigator.userAgent&lt;/a&gt; string for the WebView.&lt;/p&gt;

&lt;p&gt;The secret is that a WebView component instance can send a message to the containing React Native component through the JavaScript function &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage"&gt;window.postMessage()&lt;/a&gt;.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;info-glass&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The npm package &lt;a href="https://www.npmjs.com/package/info-glass"&gt;info-glass&lt;/a&gt; is a single JavaScript component that does one or two more things to simplify the above snippet.  The source code is on Github in one single &lt;a href="https://github.com/starpebble/info-glass/blob/master/infoglass.js"&gt;index.js&lt;/a&gt; file.&lt;/p&gt;

&lt;p&gt;Every single React Native project is different.  I simply like building purely in JavaScript because:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Developer operation simplifications.&lt;/li&gt;
&lt;li&gt;Debugging a purely JavaScript application is relatively easy.
&lt;/li&gt;
&lt;li&gt;Dust bunnies are easy to find with a JavaScript linter.
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It's not too difficult to mix in native code, it simply isn't always absolutely necessary to write native code.  If you are trying to be a humble JavaScript champion, it can't hurt to use a WebView to get the userAgent.&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>javascript</category>
      <category>opensource</category>
      <category>npm</category>
    </item>
  </channel>
</rss>
