<?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: Ryan Mercadante</title>
    <description>The latest articles on DEV Community by Ryan Mercadante (@ryanmercadante).</description>
    <link>https://dev.to/ryanmercadante</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%2F304300%2F0c9cea19-055f-412b-a22f-3e115be866a0.png</url>
      <title>DEV Community: Ryan Mercadante</title>
      <link>https://dev.to/ryanmercadante</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ryanmercadante"/>
    <language>en</language>
    <item>
      <title>Monitoring Github events with Webhooks and Google Cloud Functions</title>
      <dc:creator>Ryan Mercadante</dc:creator>
      <pubDate>Tue, 31 Mar 2020 15:32:50 +0000</pubDate>
      <link>https://dev.to/ryanmercadante/building-an-automated-github-comment-censor-with-google-cloud-functions-43a</link>
      <guid>https://dev.to/ryanmercadante/building-an-automated-github-comment-censor-with-google-cloud-functions-43a</guid>
      <description>&lt;p&gt;In this post we are going create a Google Cloud Function and a Github Webhook to automate the deletion of inappropriate comments on Github issues. We'll create a webhook that will call an HTTP function every time an issue comment is created, edited, or deleted. In order to follow along with this tutorial you will need a Google Cloud Platform account and a Github account. &lt;/p&gt;

&lt;h3&gt;
  
  
  Table of Contents
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;What is a Google Cloud Function?&lt;/li&gt;
&lt;li&gt;Developing our Function in the Console&lt;/li&gt;
&lt;li&gt;Developing our Function Locally&lt;/li&gt;
&lt;li&gt;Creating our Webhook&lt;/li&gt;
&lt;li&gt;Generating a Personal Access Token&lt;/li&gt;
&lt;li&gt;Testing our Function&lt;/li&gt;
&lt;li&gt;Deploying our Function&lt;/li&gt;
&lt;li&gt;Wrapping up&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What is a Google Cloud Function?&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Google Cloud functions are single-purpose, serverless functions that can run on demand in your cloud environment in response to events. Events include HTTP events, Cloud Pub/Sub events, and Cloud Storage events. At the time of this writing there are also a few more events currently in beta. You then create a trigger in response to the event that is emitted, and bind that trigger to a function.  &lt;/p&gt;

&lt;p&gt;Cloud Functions are useful for situations where you don't want to spin up a full server to execute some logic. All of the infrastructure and software are managed by Google so that all you have to do is write the code. This is often referred to as Functions as a Service, or FaaS, and is not unique to Google. AWS Lambda and Azure Functions are just two of the many competitors in this space. &lt;/p&gt;

&lt;h3&gt;
  
  
  Developing our Function in the Console&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;There are two ways we could go about developing our function, in the console or locally in our development environment. First, I'll demonstrate how you would go about setting it up in the console, and afterward we will actually develop in our local environment. &lt;/p&gt;

&lt;p&gt;Open up your cloud console and select a project or create a new one. Next, select Cloud Functions in the compute section of the navigation menu. Enable the API if it is not already enabled. You will also need to make sure you setup a billing account for the project to use Cloud Functions. Once you click on create function you'll see the following.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BF3_XX4k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/r2n3tzby2hhkzkf938rx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BF3_XX4k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/r2n3tzby2hhkzkf938rx.png" alt="Options for creating cloud function in GCP console"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I gave this function the name test, left the memory allocation on the default of 256 MiB, and we are using the HTTP trigger type. We are also allowing unauthenticated invocations.&lt;/p&gt;

&lt;p&gt;Next we have the advanced options. Choose the region closest to you to reduce latency. You get charged only while your function is running to the nearest 100 milliseconds. You can also set the timeout and the maximum function instances that you want running. This is useful because you can put a limit on how much your function can scale out, otherwise your function can scale out to as many instances as necessary. You will also need to select a service account that the function will assume as its identity.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SptdzFTz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/96l4ywbryvf5sxpqilmu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SptdzFTz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/96l4ywbryvf5sxpqilmu.png" alt="Advanced options for creating cloud function in GCP console"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's nice to have all the options right in front of you, but writing code in the browser like that is definitely not ideal. &lt;/p&gt;

&lt;h3&gt;
  
  
  Developing our Function Locally&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;To develop locally we are going to use the Functions Framework. This will allow us to spin up a server for our function and invoke that function in response to a request. To get started, create a new folder that will have your function and run &lt;code&gt;npm init&lt;/code&gt;. Next, run &lt;code&gt;npm install @google-cloud/functions-framework node-fetch&lt;/code&gt; and add the following script to your &lt;code&gt;package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;scripts&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;start&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;functions-framework --target=deleteGithubComment&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll need node-fetch to make a DELETE request to our github comment. Create an index.js file and add the following contents to it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetch&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;node-fetch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;inappropriateWords&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;enter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;words&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;to&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;check&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;for&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deleteGithubComment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;repository_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;issue&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;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;login&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;comment&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bodyArray&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;repository_url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/issues/comments/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Token &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TOKEN&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&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;removeComment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="nx"&gt;bodyArray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;word&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;inappropriateWords&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;removeComment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&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;removeComment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DELETE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Removed inappropriate comment on issue "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;."`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;deleted_message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;body&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;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;user&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="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error removing inappropriate comment.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;deleted_message&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="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="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`No need to remove comment. Maybe you can log this information.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;deleted_message&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="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Our function is written in Node.js. Node cloud functions use express under the hood, so you will have the familiar request and response arguments. When this function is called, it checks the contents of the comment, and checks each word against an array of inappropriate words. To keep the tutorial family friendly, I removed the words I used and added a placeholder. If you wanted to get more advanced, you could use Google's AI services to better understand the meaning of the comment and maybe catch things that a simple word check would miss. Since this is just meant to get you started I will not be doing that.&lt;/p&gt;

&lt;p&gt;If it finds a word in your array of inappropriate words, it will send a DELETE request to github to remove the comment using node-fetch. This requires getting a token from Github which we will cover in the next section. Before moving onto the next section, run &lt;code&gt;npm start&lt;/code&gt; so the functions framework can start up a server for our function. This should start up on &lt;code&gt;http://localhost:8080&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating our Webhook&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Creating our webhook requires a publicly accessible URL, but because we haven't deployed our function yet, we don't have one. In order to get around this, we are going to install an npm package called ngrok which will create a tunnel to expose our localhost to the internet. Run &lt;code&gt;npm install -g ngrok&lt;/code&gt; and once that is done, run &lt;code&gt;ngrok http 8080&lt;/code&gt;. &lt;/p&gt;

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

&lt;p&gt;Login to your Github account and select a repository that you want to use this function. Go to settings and select Webhooks.&lt;/p&gt;

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

&lt;p&gt;Click on create webhook and fill out the form like I have done. Notice how I am using the the URL provided by ngrok which will tunnel to our localhost.&lt;/p&gt;

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

&lt;p&gt;Click on Add Webhook and you're all set.&lt;/p&gt;

&lt;h3&gt;
  
  
  Generating a Personal Access Token&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Go to your user settings and then click on Developer Settings at the bottom. &lt;/p&gt;

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

&lt;p&gt;Select Personal access tokens and click on generate token. Enter some kind of descriptive note and select the &lt;code&gt;repo&lt;/code&gt; checkbox.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--62t5JqLE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/nmm0kzjbs0vtnnzze4ij.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--62t5JqLE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/nmm0kzjbs0vtnnzze4ij.png" alt="Options for generating token"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on generate token and you'll be given your token. Make sure to copy it because you'll never be able to see it again after you leave the page.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing our Function&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Head back to your code and create a &lt;code&gt;.env.yaml&lt;/code&gt; file and add the token like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;add-your-token-here&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order to get around installing the &lt;code&gt;dotenv&lt;/code&gt; package for testing and uninstalling it for deployment, just replace&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Token &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TOKEN&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Token your-token`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will change this back before we deploy our function.&lt;/p&gt;

&lt;p&gt;Now that you have everything set up you can test your function. Create a new issue in your Github repo and add a comment that shouldn't be removed by your function. Afterward, add a comment that is included in your inappropriate words array. You should see it get added, and once you refresh the page it should be deleted. You can use this setup to test other webhooks or functions that you have created. &lt;/p&gt;

&lt;h3&gt;
  
  
  Deploying our Function&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Now that we have tested our function locally, we are going to deploy our function to Google Cloud. First, DO NOT forget to remove your token from your code and replace it with &lt;code&gt;process.env.TOKEN&lt;/code&gt;. Next, from the root of your function directory run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gcloud functions deploy deleteGithubComment \
  --region us-east1 \
  --runtime nodejs10 \
  --trigger-http \
  --entry-point=deleteGithubComment \
  --env-vars-file .env.yaml \
  --allow-unauthenticated
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will deploy your function to the region us-east1 (you should change the region to the one closest to you) with the name &lt;code&gt;deleteGithubComment&lt;/code&gt;, that uses the nodejs 10 runtime, and declares it as an HTTP function. The entry point is the function in your index.js file that you want to deploy and we are letting Google Cloud know that we have environment variables that are scoped to this function. We are also allowing unauthenticated function invocations but because this function requires information from Github, nothing will happen if you just go to the URL provided by the function.&lt;/p&gt;

&lt;p&gt;Deploying will take up to two minutes but afterwards in the output you should see a URL for your function. The only thing left to do is to go back to Github and replace the ngrok URL in your webhook with the URL for your function. Now that you're deployed, test everything out once more to make sure it's still working. The first time you hit the endpoint, you'll experience what is called a cold start. If your function hasn't been run in awhile or is running for the first time, it will take a second or two for the server to spin up. After your function is called the server should stay active for some time before it's spun back down, meaning much faster response times.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wrapping up&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Cloud functions and Github webhooks can both be really powerful and you are only limited by your imagination on what you can do with them. I'd encourage you to take what you learned from this article and apply it to something else. Maybe you already have an application that could use some one-off logic wrapped up in a cloud function. Or maybe you want to automate some other aspect of Github, like emailing any user who creates a pull request with information about your project. &lt;/p&gt;

&lt;p&gt;If anything was unclear or something isn't working right, leave me a comment down below or message me and I'll do my best to help you out. If you would like to get in touch for any reason, feel free to connect with me on LinkedIn, follow me on Twitter, or send me an email. Thanks for reading!&lt;/p&gt;

</description>
      <category>cloud</category>
      <category>gcp</category>
      <category>node</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>CI/CD with Google Cloud Build</title>
      <dc:creator>Ryan Mercadante</dc:creator>
      <pubDate>Sat, 14 Mar 2020 20:46:02 +0000</pubDate>
      <link>https://dev.to/ryanmercadante/ci-cd-with-google-cloud-build-28jf</link>
      <guid>https://dev.to/ryanmercadante/ci-cd-with-google-cloud-build-28jf</guid>
      <description>&lt;p&gt;In this post we are going to cover setting up CI/CD pipeline using google cloud build. If you need to set up a GCP account or need to deploy your application, I already wrote something that should get you up and running &lt;a href="https://dev.to/ryanmercadante/deploy-your-mern-stack-application-to-google-app-engine-2g2c"&gt;https://dev.to/ryanmercadante/deploy-your-mern-stack-application-to-google-app-engine-2g2c&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Table of Contents
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;What is CI/CD?&lt;/li&gt;
&lt;li&gt;Setup Google Cloud Build&lt;/li&gt;
&lt;li&gt;Creating Build Triggers&lt;/li&gt;
&lt;li&gt;Setting up Cloud KMS&lt;/li&gt;
&lt;li&gt;Encrypting our Environment Variables&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What is CI/CD?&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;CI/CD stands for continuous integration and continuous deployment/delivery depending on who you ask. It is the process of creating pipelines to automate certain tasks such as building, testing, merging, and deploying your code. Red Hat has a great article going in depth on the nuances between the integration, delivery, and deployment phases &lt;a href="https://www.redhat.com/en/topics/devops/what-is-ci-cd" rel="noopener noreferrer"&gt;https://www.redhat.com/en/topics/devops/what-is-ci-cd&lt;/a&gt;, and I would highly recommend reading it if you're interested in understanding it better.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup Google Cloud Build&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;First head over to the GCP console and go to APIs and Services. You'll want to search the API library for Cloud Build API and enable it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fv5gkhxocga9ilrnhh6oc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fv5gkhxocga9ilrnhh6oc.png" alt="Enable cloud build api"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, go to Cloud Build in the tools section and click on settings. My application is deployed to app engine, so I am going to enable app engine. You should enable Cloud KMS as well, but we'll cover that more later in the tutorial.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F3coscclko5rjbht0r7oo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F3coscclko5rjbht0r7oo.png" alt="Enable different services for cloud build"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If your application is deployed to app engine as well, you will also need to enable the App Engine Admin API back in the API library.&lt;/p&gt;

&lt;p&gt;With that set up, let's head back to the Cloud Build page and go to Triggers. The first thing we need to do is to connect our repository. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F05i47mb6x5k1xevbays0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F05i47mb6x5k1xevbays0.png" alt="Connect our repo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select where you keep your source code (I use GitHub).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F2kvz9x6a23m8upi12rdi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F2kvz9x6a23m8upi12rdi.png" alt="Select repo source - github"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next it will ask you to authenticate with whichever provider you chose and then to select repositories to connect to cloud build. You should see a link to select repositories on GitHub (or whichever you use). You can select specific repos or all of them. I chose to only select the two projects that will be using cloud build at this moment. One is a react application and the other is an express API.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ftn09th2mgzam5i6zs6aj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ftn09th2mgzam5i6zs6aj.png" alt="Select repos to connect to cloud build"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you click save you'll be redirected back to the page you were on. Select the repos you just gave permission to, and the checkbox saying you understand the warning they provide.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fo4kmhoupetn4m5bdwfe6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fo4kmhoupetn4m5bdwfe6.png" alt="Checkbox for warning"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The last step to create a push trigger is optional and we are going to handle that in the next section, so skip that for now. &lt;/p&gt;

&lt;h3&gt;
  
  
  Creating Build Triggers&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The next step in the process is to create our build triggers. Cloud Build offers 120 minutes of free build time per day, but after that it is $0.003 per minute. This is a very generous free tier, and for personal learning projects, even if you did go over the free tier limit it is very unlikely you would rack up any excessive cost. With this in mind though, we are only going to create one trigger for each repo I added. The trigger will install dependencies, build our app, and deploy it. You can also include other build steps like linting, testing, etc. &lt;/p&gt;

&lt;p&gt;With that out of the way let's build our first trigger. Go to the cloud build dashboard and click the button to set up build triggers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F3cits3mwsmigwyke1s8m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F3cits3mwsmigwyke1s8m.png" alt="Page to build a trigger"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We are going to name the trigger &lt;code&gt;push-to-master&lt;/code&gt;, and this needs to be unique within the project. There are three trigger types; branch, tag, and pull request. In this tutorial we are going to use the branch trigger type. In the branch section just type in master so it knows to only trigger when you push to the master branch. We are going to ignore a few optional sections which allow you to trigger based on changes to specific files. For our build configuration we are going to use a cloud build configuration file; &lt;code&gt;cloudbuild.yaml&lt;/code&gt;. It should default the file location to the root of your project. This is what our file looks like.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gcr.io/cloud-builders/npm"&lt;/span&gt;
  &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;install"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gcr.io/cloud-builders/npm"&lt;/span&gt;
  &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;run"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;build"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gcr.io/cloud-builders/gcloud"&lt;/span&gt;
  &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;app"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;deploy"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1600s"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This build uses two different cloud builders; &lt;code&gt;npm&lt;/code&gt; and &lt;code&gt;gcloud&lt;/code&gt;. Cloud builders are just container images that allow you to run commands within the context of the builder. The &lt;code&gt;args&lt;/code&gt; field takes a list of arguments that the builder will run. So using the npm builder with the arguments &lt;code&gt;run&lt;/code&gt; and &lt;code&gt;build&lt;/code&gt; is the same as running &lt;code&gt;npm run build&lt;/code&gt; in your terminal. If you click on the history tab, you should see a list of your previous builds. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fk2khdvkyrfwbgfi77a6t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fk2khdvkyrfwbgfi77a6t.png" alt="Previous builds"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can further inspect the build process by clicking on the build Id in the build column.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fdvwoxrzytgcxy9nly8qw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fdvwoxrzytgcxy9nly8qw.png" alt="Build steps"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Clicking into these tabs will give you more insight into what was going on behind the scenes. This is especially useful if your build failed.&lt;/p&gt;

&lt;p&gt;That was the build trigger for my react app. The &lt;code&gt;cloudbuild.yaml&lt;/code&gt; is going to look a little bit different for my express API because I am using environment variables that need to be encrypted. In order to do this properly, we are going to need to enable the Cloud Key Management Service (KMS) API.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up Cloud KMS&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Cloud KMS will allow us to create encryption keys to protect secrets and other sensitive data that we need to store in the Google Cloud Platform. More specifically, we are going to use it to encrypt our &lt;code&gt;.env&lt;/code&gt; file because that has our database URI.&lt;/p&gt;

&lt;p&gt;Search for Cloud Key Management Service (KMS) API in the API library and enable it. Before we can create a cryptographic key, we need to create a keyring. We are going to be using a &lt;code&gt;gcloud&lt;/code&gt; command for this, otherwise you can create a keyring using the console.&lt;/p&gt;

&lt;p&gt;Run &lt;code&gt;gcloud kms keyrings create cloudbuild-keys --location global&lt;/code&gt; in your terminal to create your keyring. I named mine &lt;code&gt;cloudbuild-keys&lt;/code&gt; but you can name it whatever you'd like. You should see this in the console.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fwqvow23yzvrf9u0iqi6d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fwqvow23yzvrf9u0iqi6d.png" alt="View keyrings"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next we are going to create our key. Run the command &lt;code&gt;gcloud kms keys create api-key --location global --keyring cloudbuild-keys --purpose encryption&lt;/code&gt;. You should see the key in the keyring you created.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F70k3z675g5uqhx4ps1uu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F70k3z675g5uqhx4ps1uu.png" alt="View keys"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want to use the console to create your keyring and keys, it's pretty straightforward and I have provided pictures to show you the console equivalent of our &lt;code&gt;gcloud&lt;/code&gt; commands.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fbz630ah5pges71nc3nyf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fbz630ah5pges71nc3nyf.png" alt="Create keyring - console"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ftg7qc95t8bzdj1r69bvm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ftg7qc95t8bzdj1r69bvm.png" alt="Create key - console"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Encrypting our Environment Variables&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Next, we are going to encrypt our &lt;code&gt;.env&lt;/code&gt; file so that we can use it in our build. Just to reiterate, I have to do this for my deployed API because my environment variables include my database URI, which should be kept private. I didn't have any sensitive information for the client which is why none of these steps were necessary.&lt;/p&gt;

&lt;p&gt;Run the following command in the root folder of your project but make sure to change out my project specific information with your project specific information. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;gcloud kms encrypt \&lt;br&gt;
  --plaintext-file=config/config.env \  &lt;br&gt;
  --ciphertext-file=config/config.env.enc \&lt;br&gt;
  --location=global \&lt;br&gt;
  --keyring=cloudbuild-keys \&lt;br&gt;
  --key=api-key&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;It is safe to commit your encrypted file to source code, but make sure you add your unencrypted file to your &lt;code&gt;.gitignore&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now that we have successfully encrypted our sensitive information, we can use it in our CI/CD pipeline. My &lt;code&gt;cloudbuild.yaml&lt;/code&gt; file for my API looks like this. Once again you'll want to remove my project specific information and add yours instead. This will decrypt our &lt;code&gt;config.env.enc&lt;/code&gt; at build time so that we can use the decrypted file in our workspace.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# Decrypt our env file&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcr.io/cloud-builders/gcloud&lt;/span&gt;
  &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;kms&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;decrypt&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--ciphertext-file=config/config.env.enc&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--plaintext-file=config/config.env&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--location=global&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--keyring=cloudbuild-keys&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--key=api-key&lt;/span&gt;

  &lt;span class="c1"&gt;# Deploy our app&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gcr.io/cloud-builders/gcloud"&lt;/span&gt;
  &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;app"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;deploy"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1600s"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hopefully all of these steps worked for you and you have a fully functioning CI/CD pipeline for your application! If anything was unclear or something isn't working right, leave me a comment down below or message me and I'll do my best to help you out. If you would like to get in touch for any reason, feel free to connect with me on LinkedIn, follow me on Twitter, or send me an email. Thanks for reading!&lt;/p&gt;

</description>
      <category>gcp</category>
      <category>devops</category>
      <category>cloud</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Deploy your MERN Stack Application to Google App Engine</title>
      <dc:creator>Ryan Mercadante</dc:creator>
      <pubDate>Sun, 01 Mar 2020 20:56:44 +0000</pubDate>
      <link>https://dev.to/ryanmercadante/deploy-your-mern-stack-application-to-google-app-engine-2g2c</link>
      <guid>https://dev.to/ryanmercadante/deploy-your-mern-stack-application-to-google-app-engine-2g2c</guid>
      <description>&lt;p&gt;In this post we are going to walk through the steps of deploying a MERN stack application on the Google Cloud Platform. We will be deploying our React frontend and Node backend as separate services using Google App Engine, their Platform as a Service offering. In order to do this we are going to need to sign up for a GCP account. If you already have an account and have configured gcloud, you can skip the first couple of sections. Note: this tutorial assumes you have a demo application already built. If you do not, feel free to grab my code from &lt;a href="https://github.com/ryanmercadante/Users-app-gcp" rel="noopener noreferrer"&gt;https://github.com/ryanmercadante/Users-app-gcp&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Table of Contents
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Signing up for GCP&lt;/li&gt;
&lt;li&gt;Creating our Project&lt;/li&gt;
&lt;li&gt;Setup the Cloud SDK&lt;/li&gt;
&lt;li&gt;Deploying our React Application&lt;/li&gt;
&lt;li&gt;Setup MongoDB using Atlas&lt;/li&gt;
&lt;li&gt;Deploying our Node.js API&lt;/li&gt;
&lt;li&gt;Recap&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Signing up for GCP&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Head over to &lt;a href="https://cloud.google.com/" rel="noopener noreferrer"&gt;https://cloud.google.com/&lt;/a&gt; and you should be presented with a page that looks something similar to this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Flsoefe5zisythogxwaa1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Flsoefe5zisythogxwaa1.png" alt="GCP Signup Screen"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on the ‘Get started for free’ button and follow the steps. You’ll need to create a gmail account if you don’t have one already. Afterward you should be presented with a screen to try Google Cloud Platform for free. They have a very generous free plan, which gives you $300 dollars worth of credits for free and no auto charge if you run out. You have to manually upgrade to a paid account.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fe34b80x3cbrt1jxlc32d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fe34b80x3cbrt1jxlc32d.png" alt="GCP Signup Screen 1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The second page of signing up is fairly straight forward, just make sure under ‘Account type’ you sign up for an Individual account and not a Business account.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fh9wugo8t8gf5fsyz3par.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fh9wugo8t8gf5fsyz3par.png" alt="GCP Signup Screen 2a"&gt;&lt;/a&gt; &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ffwde0oqsvc2hk2web0b5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ffwde0oqsvc2hk2web0b5.png" alt="GCP Signup Screen 2b"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Upon completion of the form, you should be redirected to the google console homepage. &lt;/p&gt;

&lt;h3&gt;
  
  
  Creating our Project&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Now that we have an account setup, let’s create a new project. In the top navigation bar you should see a dropdown to select a project. Click on that and select New Project in the top right corner. You can name the project whatever you’d like, and google will automatically create a globally unique project id for you. You can edit this now but once you create the project you won’t be able to change it. I usually like to edit my project ids so they are a little bit easier to remember, such as the project name with my initials at the end of it. We are not going to select an organization at this time. Click on create when you are done with this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ftflc48aeevftovkqslc7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ftflc48aeevftovkqslc7.png" alt="Create new GCP Project"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Head over to the navigation menu and select App Engine inside of the Compute section. First select download the SDK, we’ll be using this later to deploy our app to App Engine. When you are done install the SDK, come back to this page and select Create Application.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fcx8c8dz6aakn15ohoqev.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fcx8c8dz6aakn15ohoqev.png" alt="App Engine Dashboard Before Project"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select a region close to you (I will be using us-east1). On the next page we are going to select Node.js as our language, and Standard as our environment instead of Flexible. We will be using the standard environment so that our app can scale down to 0 instances when there is no traffic. This will allow us to run the app for free or for very little cost which is ideal in my case. You will want to use the flexible environment if you are using Docker containers and your app receives regular traffic, or if you want to use a programming language that the standard environment doesn’t support. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fw4yuv3x2shy0rs4sawac.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fw4yuv3x2shy0rs4sawac.png" alt="Region selection"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7d1vq0suyuyxgbhbh8h7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7d1vq0suyuyxgbhbh8h7.png" alt="Environment and language selection"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you’d like to see a full list of differences between the standard and flexible environments head over to &lt;a href="https://cloud.google.com/appengine/docs/the-appengine-environments" rel="noopener noreferrer"&gt;https://cloud.google.com/appengine/docs/the-appengine-environments&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ffqx0b25z247joyikhgoa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ffqx0b25z247joyikhgoa.png" alt="Download SDK"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you haven’t already done so go ahead and download the SDK, otherwise select I’ll do this later.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup the Cloud SDK&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Open up your terminal of choice and run ‘gcloud init.’ This will run you through the steps of configuring gcloud, their CLI tool. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fek3hqnf3l3wlklqhkpcb.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fek3hqnf3l3wlklqhkpcb.PNG" alt="Initialize gcloud"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Deploying our React Application&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Congrats on making it this far, now we get to the fun part. Head into the root directory of your react application and create an app.yaml file. This is where we configure our application settings for App Engine. It contains information about our code, runtime, URL paths and more. This is what our app.yaml file is going to look like.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fl40x25fyk6qgg0w8ligg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fl40x25fyk6qgg0w8ligg.png" alt="React app app.yaml file"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At the very least, our app.yaml needs to include a runtime, of which we are using nodejs10. The handlers will handle our static files that are in our build folder. If you haven’t created a build of your application yet you will want to do so now.&lt;br&gt;
Because this is our first service that we are deploying, it will be deployed as the default service. Let’s go back to our terminal and run the command ‘gcloud app deploy’ from the root of our project. You will be asked if you want to continue, if you do enter Y for yes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fqc24h16py3vpm26vqe3d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fqc24h16py3vpm26vqe3d.png" alt="gcloud app deploy command"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I am not deploying anything new so I have 0 files uploaded, but yours should be more than 0 if its your first time deploying your application or if you added something new since your last deploy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fehw9sg2rmzlfee67vguf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fehw9sg2rmzlfee67vguf.png" alt="Beginning deployment of service"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that your frontend is done deploying, run ‘gcloud app browse’ and checkout your deployed app! If you head back over to your GCP console and to the App Engine dashboard, you’ll see some statistics about your app.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fcp43czpopvtdm6szverw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fcp43czpopvtdm6szverw.png" alt="App Engine statistics"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If/when you deploy your app again, app engine won’t automatically delete your previous version that you deployed. You can see the different versions of your app under the versions tab. This is useful if you want to split traffic between different versions of your application for things like A/B testing. I won't cover that in this tutorial, but you can read more about that here &lt;a href="https://cloud.google.com/appengine/docs/standard/nodejs/splitting-traffic" rel="noopener noreferrer"&gt;https://cloud.google.com/appengine/docs/standard/nodejs/splitting-traffic&lt;/a&gt;. App Engine is smart enough to move traffic to the most recently deployed version of your application. If you select the older versions you can delete them if you choose to do so. &lt;/p&gt;

&lt;h3&gt;
  
  
  Setup MongoDB using Atlas&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Before we deploy our node API, we should setup a database using MongoDB Atlas. Go to &lt;a href="https://www.mongodb.com/cloud/atlas" rel="noopener noreferrer"&gt;https://www.mongodb.com/cloud/atlas&lt;/a&gt; and either sign in to your account, or create one. After signing up you’ll be presented with a page to create an organization. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7diqr5667efa163vvp62.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7diqr5667efa163vvp62.png" alt="Create organization for MongoDB Atlas"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since this is just an account for small personal projects, I am going to name my organization test. Click next, and then add a new user and give them the role of Organization Project Creator, otherwise you won’t be able to create any projects.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fnl3baw9nj0v29voiwrx6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fnl3baw9nj0v29voiwrx6.png" alt="Create organization project creator"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Afterward you’ll be redirected to your dashboard. Select create a project, and give your project a name. On the next page, when you click to add a new member, the user you created earlier should be presented as an option. Select that user and give them the Project Data Access Admin role. Because this is just a demo app, I am not going to worry about giving this user admin privileges, but for a real application with potentially sensitive information, you should be diligent about how you assign database roles to users.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F29fnzgw25xmowihv9i43.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F29fnzgw25xmowihv9i43.png" alt="User database role"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now it’s time to build a cluster. Select build a cluster in the center of the screen and then select the free shared clusters. Make sure to select Google Cloud Platform and the region closest to you that clearly states ‘Free Tier Available.’&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F5dxu8l6fs791rnxcnjpp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F5dxu8l6fs791rnxcnjpp.png" alt="Select cloud provider"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The M0 sandbox cluster tier is free forever so that is the one we’ll be selecting. You can rename the cluster to whatever you would like.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F2sprxtllhhi3lymrkip6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F2sprxtllhhi3lymrkip6.png" alt="Create cluster name"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It should take a few minutes for your cluster to get setup, but once it does, click connect and whitelist 0.0.0.0. This will allow traffic from anywhere. This is fine for our demo app but for a production app you would probably never do this. You will also need to create a MongoDB user.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F90kr1sz0xz0uzku0igbd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F90kr1sz0xz0uzku0igbd.png" alt="Whitelist IP and create database user"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next we need to choose a connection method. Select connect your application, and copy the connection string they provide for you to use in your application where needed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fwgnh57aywnbr2p3cigob.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fwgnh57aywnbr2p3cigob.png" alt="MongoDB connection method"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Congrats! You now have your MongoDB cluster up and running. It has one primary node and two secondary nodes for data replication.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deploying our Node.js API&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Deploying our node api will follow similar steps we took to deploy our React frontend. We will create another app.yaml and place it in the root directory of our node application.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fti0xa3nzh7f5mlkeebj6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fti0xa3nzh7f5mlkeebj6.png" alt="Node API app.yaml"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the app.yaml for our react frontend, we didn’t name our service because it was our default service. Since this is not our default service, let’s just name it api. Once again we are use the nodejs10 runtime. Dealing with environment variables that are meant to be secret, such as our MongoDB URI, is a little bit tricky. The most straight forward solution I found was to create a separate yaml file for your environment variables, add that to your .gitignore file so it isn’t tracked, and use the includes element. Run the ‘gcloud app deploy’ command and your api will be deployed to App Engine. If you check the services tab, you should see both the default service and the api service, and how many version of each are deployed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F0c0th03gyu0cjhwvuc3m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F0c0th03gyu0cjhwvuc3m.png" alt="App engine services tab"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Both your frontend and backend are deployed now and you can go to the url that app engine created for you!&lt;/p&gt;

&lt;h3&gt;
  
  
  Recap&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;In this tutorial you learned how to deploy a React application, express API, and MongoDB database to Google Cloud Platform using Google App Engine and MongoDB Atlas. The steps you followed here are a great starting point for personal projects and I encourage you to build upon it. This application is most definitely not secure, and if you wanted to start building applications for clients utilizing GCP, &lt;a href="https://cloud.google.com/appengine/docs/standard/nodejs/application-security" rel="noopener noreferrer"&gt;https://cloud.google.com/appengine/docs/standard/nodejs/application-security&lt;/a&gt; is the first place I would start. In a future tutorial I plan to write about things like adding a custom domain, utilizing CI/CD, and more. If you have any questions, comments, or general feedback let me know in the comments below. If you would like to get in touch for any reason, feel free to connect with me on LinkedIn, follow me on Twitter, or send me an email. Thanks for reading!&lt;/p&gt;

</description>
      <category>react</category>
      <category>node</category>
      <category>mongodb</category>
      <category>appengine</category>
    </item>
  </channel>
</rss>
