<?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>Forem: Rajitha Gunathilake</title>
    <description>The latest articles on Forem by Rajitha Gunathilake (@rizkyrajitha).</description>
    <link>https://forem.com/rizkyrajitha</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%2F247664%2Fbb29688b-6d69-444c-b5fb-d5b4365bb671.jpeg</url>
      <title>Forem: Rajitha Gunathilake</title>
      <link>https://forem.com/rizkyrajitha</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/rizkyrajitha"/>
    <language>en</language>
    <item>
      <title>Automate adding screenshots to GitHub commits and pull requests  </title>
      <dc:creator>Rajitha Gunathilake</dc:creator>
      <pubDate>Sat, 04 Dec 2021 06:54:34 +0000</pubDate>
      <link>https://forem.com/rizkyrajitha/automate-adding-screenshots-to-github-commits-and-pull-requests-37gh</link>
      <guid>https://forem.com/rizkyrajitha/automate-adding-screenshots-to-github-commits-and-pull-requests-37gh</guid>
      <description>&lt;h3&gt;
  
  
  My Workflow
&lt;/h3&gt;

&lt;p&gt;This workflow will run puppeteer and capture screenshots of a web application, and then add screenshots to commit comments or pull request comments. This will ease the process of verifying the web app UI state.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://github.com/RizkyRajitha/linkin/blob/master/.github/workflows/browsertesting.yml" rel="noopener noreferrer"&gt;workflow file&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Submission Category:
&lt;/h3&gt;

&lt;p&gt;Maintainer Must-Haves&lt;/p&gt;

&lt;h3&gt;
  
  
  Yaml File
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Browser-Testing&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Browser-Testing&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="c1"&gt;# Service containers to run postgres&lt;/span&gt;
    &lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

      &lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;

        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
          &lt;span class="na"&gt;POSTGRES_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
          &lt;span class="na"&gt;POSTGRES_DB&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;

        &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;-&lt;/span&gt;
          &lt;span class="s"&gt;--health-cmd pg_isready&lt;/span&gt;
          &lt;span class="s"&gt;--health-interval 10s&lt;/span&gt;
          &lt;span class="s"&gt;--health-timeout 5s&lt;/span&gt;
          &lt;span class="s"&gt;--health-retries 5&lt;/span&gt;
        &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;5432:5432&lt;/span&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="s"&gt;Checkout repository&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&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;Set up Node.js 14.x&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;14.x&lt;/span&gt;
      &lt;span class="c1"&gt;# install puppeteer dependency libraries using apt&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;install puppeteer libraries&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;sudo apt-get update&lt;/span&gt;
          &lt;span class="s"&gt;sudo apt-get install -y libgbm1&lt;/span&gt;
      &lt;span class="c1"&gt;# since puppeteer is large in size caching will reduce the runtime &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;Cache node modules&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v2&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;cache-name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cache-node-modules&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;node_modules"&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}&lt;/span&gt;
          &lt;span class="na"&gt;restore-keys&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;${{ runner.os }}-build-${{ env.cache-name }}-&lt;/span&gt;
            &lt;span class="s"&gt;${{ runner.os }}-build-&lt;/span&gt;
            &lt;span class="s"&gt;${{ runner.os }}-&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;Install dependencies&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm install&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;Install puppeteer&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm install puppeteer&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;Migrate database&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run prismamigrateprod&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;NODE_ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;
          &lt;span class="na"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;postgres://postgres:postgres@localhost:5432/postgres"&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;Seed database&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run seed&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;NODE_ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;
          &lt;span class="na"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;postgres://postgres:postgres@localhost:5432/postgres"&lt;/span&gt;

      &lt;span class="c1"&gt;# run a bash script to start the server and then run puppeteer to capture the screenshots&lt;/span&gt;
      &lt;span class="c1"&gt;# after capturing screenshots , they will be uploaded to cloudinary image service and pass the image url to next step using environment variables &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;Run browser testing script&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;browser-testing&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;HASHSALT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;123&lt;/span&gt;
          &lt;span class="na"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;postgres://postgres:postgres@localhost:5432/postgres"&lt;/span&gt;
          &lt;span class="na"&gt;CLOUDINARY_CLOUD_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.CLOUDINARY_CLOUD_NAME }}&lt;/span&gt;
          &lt;span class="na"&gt;CLOUDINARY_API_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.CLOUDINARY_API_KEY }}&lt;/span&gt;
          &lt;span class="na"&gt;CLOUDINARY_API_SECRET&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.CLOUDINARY_API_SECRET }}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;chmod +x run-browser-testing.sh&lt;/span&gt;
          &lt;span class="s"&gt;./run-browser-testing.sh&lt;/span&gt;

      &lt;span class="c1"&gt;# add comment to commit or pull request &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;Add comment&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/github-script@v5&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;SHA&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.sha }}&lt;/span&gt;
          &lt;span class="na"&gt;commentBody&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.commentBody }}&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;github-token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
          &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;

            &lt;span class="s"&gt;let buffercommentBody = process.env.commentBody&lt;/span&gt;
            &lt;span class="s"&gt;let utf8commentBody = new Buffer.from(buffercommentBody, "base64").toString("utf8");&lt;/span&gt;
            &lt;span class="s"&gt;console.log(utf8commentBody);&lt;/span&gt;

            &lt;span class="s"&gt;github.rest.repos.createCommitComment({&lt;/span&gt;
              &lt;span class="s"&gt;owner: context.repo.owner,&lt;/span&gt;
              &lt;span class="s"&gt;repo: context.repo.repo,&lt;/span&gt;
              &lt;span class="s"&gt;commit_sha: process.env.SHA,&lt;/span&gt;
              &lt;span class="s"&gt;body: utf8commentBody&lt;/span&gt;
            &lt;span class="s"&gt;})&lt;/span&gt;

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

&lt;/div&gt;


&lt;p&gt;Main workflow steps boils down to&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run puppeteer and capture the screenshots.&lt;/li&gt;
&lt;li&gt;upload the screenshots to a external image service (in this case cloudinary) and get the image url.&lt;/li&gt;
&lt;li&gt;Create markdown file as string using the image urls.&lt;/li&gt;
&lt;li&gt;Convert markdown file string into base64 encoded string to ease passing to the environment variables (easier to pass control characters such as newlines).&lt;/li&gt;
&lt;li&gt;Use  &lt;code&gt;actions/github-script&lt;/code&gt; action &lt;code&gt;createCommitComment&lt;/code&gt; method to post the comment to commit or pull request. &lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Additional Info
&lt;/h3&gt;

&lt;p&gt;Used in &lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&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%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/RizkyRajitha" rel="noopener noreferrer"&gt;
        RizkyRajitha
      &lt;/a&gt; / &lt;a href="https://github.com/RizkyRajitha/linkin" rel="noopener noreferrer"&gt;
        linkin
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Linkin is a customizable self hosted link tree platform.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;p&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/38534289/119221855-0522c380-bb0f-11eb-8fee-c335fd0ff67c.png"&gt;&lt;img width="400" height="400" alt="Linkin logo" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F38534289%2F119221855-0522c380-bb0f-11eb-8fee-c335fd0ff67c.png"&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Linkin · &lt;a href="https://deepscan.io/dashboard#view=project&amp;amp;tid=14086&amp;amp;pid=17178&amp;amp;bid=386441" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/7cd53f67b6b58889904d387696e5c86180eb156b64c4d5601340bb1c3df24fe4/68747470733a2f2f646565707363616e2e696f2f6170692f7465616d732f31343038362f70726f6a656374732f31373137382f6272616e636865732f3338363434312f62616467652f67726164652e737667" alt="DeepScan grade"&gt;&lt;/a&gt; &lt;a href="https://codecov.io/gh/RizkyRajitha/linkin" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/ee870c6ad7154f2847d900eb8fd70ee8c1c3f2838ce9e514a152714e7ca8ac6d/68747470733a2f2f636f6465636f762e696f2f67682f52697a6b7952616a697468612f6c696e6b696e2f6272616e63682f6d61737465722f67726170682f62616467652e7376673f746f6b656e3d44504533595655595557" alt="codecov"&gt;&lt;/a&gt; &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/a0ac4580a3fb3ce0d2d8ac1f45a8fb42cfa62e88ce63345391ec1cc5e63098db/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f72697a6b7972616a697468612f6c696e6b696e3f3f7374796c653d706c6173746963"&gt;&lt;img src="https://camo.githubusercontent.com/a0ac4580a3fb3ce0d2d8ac1f45a8fb42cfa62e88ce63345391ec1cc5e63098db/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f72697a6b7972616a697468612f6c696e6b696e3f3f7374796c653d706c6173746963" alt="license"&gt;&lt;/a&gt; &lt;a href="https://github.com/RizkyRajitha/linkin/actions/workflows/coverage.yml" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/RizkyRajitha/linkin/actions/workflows/coverage.yml/badge.svg?branch=master" alt="Code Coverage"&gt;&lt;/a&gt;
&lt;/h1&gt;
&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Linkin is a customizable self-hosted link tree application.&lt;/h2&gt;
&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Free and Open Source 💯&lt;/h3&gt;
&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Self Hosted, you own your data 💽&lt;/h3&gt;

&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Customize your link tree with few clicks using a feature-rich dashboard 🤖&lt;/h3&gt;

&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;SEO friendly design built using Next js 🕸️&lt;/h3&gt;

&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Supports one-click deploy using multiple cloud providers 🚀&lt;/h3&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="http://linkindemo.vercel.app/" rel="nofollow noopener noreferrer"&gt;View Demo&lt;/a&gt;
&lt;br&gt;
&lt;a href="http://linkindemo.vercel.app/admin" rel="nofollow noopener noreferrer"&gt;Demo Admin&lt;/a&gt;
&lt;code&gt;http://linkindemo.vercel.app/admin&lt;/code&gt;
&lt;br&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Demo username = &lt;code&gt;admin&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Demo password = &lt;code&gt;linkin123&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;br&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/b12fc6f10beab9835c82983fbbeee63b098646df85d6186ef9fb22bdd051a870/68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f64696a6a716673746f2f696d6167652f75706c6f61642f76313633323933303237382f6c696e6b696e2f6c696e6b696e5f79726772336b2e676966"&gt;&lt;img src="https://camo.githubusercontent.com/b12fc6f10beab9835c82983fbbeee63b098646df85d6186ef9fb22bdd051a870/68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f64696a6a716673746f2f696d6167652f75706c6f61642f76313633323933303237382f6c696e6b696e2f6c696e6b696e5f79726772336b2e676966" alt="linkin gif" width="100%"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;br&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Deploy with Vercel&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a href="https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FRizkyRajitha%2Flinkin&amp;amp;env=DATABASE_URL%2CHASHSALT%2CNODE_ENV" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/20bea215d35a4e28f2c92ea5b657d006b087687486858a40de2922a4636301ab/68747470733a2f2f76657263656c2e636f6d2f627574746f6e" alt="Deploy with Vercel"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Deploy with Heroku&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a href="https://heroku.com/deploy?template=https://github.com/RizkyRajitha/linkin" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/dc2056acd0e6ff421bfc2b129417f4f832d626c61d1c083221211d8503a429f7/68747470733a2f2f7777772e6865726f6b7563646e2e636f6d2f6465706c6f792f627574746f6e2e737667" alt="Deploy"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Deploy with Railway&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a href="https://railway.app/new/template?template=https%3A%2F%2Fgithub.com%2FRizkyRajitha%2Flinkin&amp;amp;plugins=postgresql&amp;amp;envs=HASHSALT%2CPORT%2CRAILWAY&amp;amp;HASHSALTDesc=Random+secret+HASHSALT+for+JWT+and+password+encryption&amp;amp;PORTDesc=Exposed+Port+in+Dockerfile&amp;amp;RAILWAYDesc=migrate+and+seed+the+database+in+railway+.+done+in+docker+image+build+time&amp;amp;PORTDefault=3000&amp;amp;RAILWAYDefault=1" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/e4002051668809c220b10ad92ddd6fb87f365d8cd4ff470e0aeca3bc5b05450e/68747470733a2f2f7261696c7761792e6170702f627574746f6e2e737667" alt="Deploy on Railway"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/38534289/119221911-4ca94f80-bb0f-11eb-94ff-31f1c3a51d06.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F38534289%2F119221911-4ca94f80-bb0f-11eb-94ff-31f1c3a51d06.png" alt="Screenshot_2021-05-22 LinkIn's Link tree Page"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/38534289/119221942-7d898480-bb0f-11eb-9175-5e139fa57f0a.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F38534289%2F119221942-7d898480-bb0f-11eb-9175-5e139fa57f0a.png" alt="Screenshot_2021-05-22 Linkin Dashboard"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/38534289/119221939-7c585780-bb0f-11eb-944f-514beb5573b7.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F38534289%2F119221939-7c585780-bb0f-11eb-944f-514beb5573b7.png" alt="Screenshot_2021-05-22 Linkin Dashboard"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Getting started&lt;/h2&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Deploy in Vercel
&lt;ul&gt;
&lt;li&gt;set environment variables
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;DATABASE_URL&lt;/code&gt; - &lt;strong&gt;Postgres&lt;/strong&gt; database url&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;HASHSALT&lt;/code&gt; - random secret key&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;NODE_ENV&lt;/code&gt; - set NODE_ENV to &lt;code&gt;production&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;after successfully deploying visit &lt;code&gt;youdomain/admin&lt;/code&gt; to view admin login&lt;/li&gt;
&lt;li&gt;use default login credentials
&lt;ul&gt;
&lt;li&gt;username = &lt;code&gt;admin&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;password = &lt;code&gt;linkin123&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;after a successfull login you will be able to see above admin dashboard.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;br&gt;
&lt;br&gt;
&lt;ul&gt;
&lt;li&gt;Deploy in Heroku
&lt;ul&gt;
&lt;li&gt;set environment variables
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;DATABASE_URL&lt;/code&gt; - &lt;strong&gt;Postgres&lt;/strong&gt; database url&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;HASHSALT&lt;/code&gt; - random secret key&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;after successfully deploying visit &lt;code&gt;youdomain/admin&lt;/code&gt; to…&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/RizkyRajitha/linkin" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;&lt;a href="https://github.com/RizkyRajitha/linkin/commit/b463546d540990afc1ec7f9f77a945aa3f4d0f29" rel="noopener noreferrer"&gt;Example commit comment&lt;/a&gt;&lt;/p&gt;

</description>
      <category>actionshackathon21</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Deploy Appwrite to AWS using Terraform and Ansible</title>
      <dc:creator>Rajitha Gunathilake</dc:creator>
      <pubDate>Thu, 07 Oct 2021 15:31:05 +0000</pubDate>
      <link>https://forem.com/rizkyrajitha/deploy-appwrite-to-aws-using-terraform-and-ansible-4af</link>
      <guid>https://forem.com/rizkyrajitha/deploy-appwrite-to-aws-using-terraform-and-ansible-4af</guid>
      <description>&lt;p&gt;Hi everyone,&lt;/p&gt;

&lt;p&gt;This is a tutorial I am going to walk through how to deploy &lt;a href="https://appwrite.io/" rel="noopener noreferrer"&gt;Appwrite&lt;/a&gt; to &lt;a href="https://aws.amazon.com/ec2/" rel="noopener noreferrer"&gt;AWS EC2&lt;/a&gt; instance using &lt;a href="https://www.terraform.io/" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt; and &lt;a href="https://www.ansible.com/" rel="noopener noreferrer"&gt;Ansible&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Appwrite is a self-hosted solution that provides developers with a set of easy-to-use and integrate REST APIs to manage their core backend needs.&lt;/p&gt;

&lt;p&gt;We will use Terraform to provision the underlying infrastructure and manage configurations and deploy appwrite using Ansible.&lt;/p&gt;

&lt;p&gt;code for this tutorial can be found in &lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&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%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/RizkyRajitha" rel="noopener noreferrer"&gt;
        RizkyRajitha
      &lt;/a&gt; / &lt;a href="https://github.com/RizkyRajitha/appwriteiac" rel="noopener noreferrer"&gt;
        appwriteiac
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;p&gt;Hi everyone,&lt;/p&gt;
&lt;p&gt;This is a tutorial I am going to walk through how to deploy &lt;a href="https://appwrite.io/" rel="nofollow noopener noreferrer"&gt;Appwrite&lt;/a&gt; to &lt;a href="https://aws.amazon.com/ec2/" rel="nofollow noopener noreferrer"&gt;AWS EC2&lt;/a&gt; instance using &lt;a href="https://www.terraform.io/" rel="nofollow noopener noreferrer"&gt;Terraform&lt;/a&gt; and &lt;a href="https://www.ansible.com/" rel="nofollow noopener noreferrer"&gt;Ansible&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Appwrite is a self-hosted solution that provides developers with a set of easy-to-use and integrate REST APIs to manage their core backend needs.&lt;/p&gt;
&lt;p&gt;We will use Terraform to provision the underlying infrastructure and manage configurations and deploy appwrite using Ansible.&lt;/p&gt;
&lt;p&gt;code for this tutorial can be found in&lt;/p&gt;
&lt;p&gt;{% github RizkyRajitha/appwriteiac %}&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;prerequisites&lt;/h3&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;aws account &lt;a href="https://aws.amazon.com/free/" rel="nofollow noopener noreferrer"&gt;AWS free tier&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Terraform installed &lt;a href="https://learn.hashicorp.com/tutorials/terraform/install-cli?in=terraform/aws-get-started" rel="nofollow noopener noreferrer"&gt;Terraform official tutorial&lt;/a&gt; and
setup with aws credentials &lt;a href="https://learn.hashicorp.com/tutorials/terraform/aws-build?in=terraform/aws-get-started" rel="nofollow noopener noreferrer"&gt;Terraform official tutorial&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Ansible installed &lt;a href="https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html" rel="nofollow noopener noreferrer"&gt;Ansible official installation guide&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;AWS Key pair for SSH connection &lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-key-pairs.html" rel="nofollow noopener noreferrer"&gt;AWS UserGuide&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Provisioning infrastructure with Terraform&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;we'll start by provisioning the infrastructure needed to deploy appwrite using terraform.&lt;/p&gt;
&lt;p&gt;in this tutorial, we will be provision&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;VPC and subnet to run EC2 instance&lt;/li&gt;
&lt;li&gt;Internet gateway to expose subnet to internet&lt;/li&gt;
&lt;li&gt;route table and…&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/RizkyRajitha/appwriteiac" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h3&gt;
  
  
  prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;aws account &lt;a href="https://aws.amazon.com/free/" rel="noopener noreferrer"&gt;AWS free tier&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Terraform installed &lt;a href="https://learn.hashicorp.com/tutorials/terraform/install-cli?in=terraform/aws-get-started" rel="noopener noreferrer"&gt;Terraform official tutorial&lt;/a&gt; and
setup with aws credentials &lt;a href="https://learn.hashicorp.com/tutorials/terraform/aws-build?in=terraform/aws-get-started" rel="noopener noreferrer"&gt;Terraform official tutorial&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Ansible installed &lt;a href="https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html" rel="noopener noreferrer"&gt;Ansible official installation guide&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;AWS Key pair for SSH connection &lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-key-pairs.html" rel="noopener noreferrer"&gt;AWS UserGuide&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Provisioning infrastructure with Terraform
&lt;/h2&gt;

&lt;p&gt;we'll start by provisioning the infrastructure needed to deploy appwrite using terraform.&lt;/p&gt;

&lt;p&gt;in this tutorial, we will be provision&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;VPC and subnet to run EC2 instance&lt;/li&gt;
&lt;li&gt;Internet gateway to expose subnet to internet&lt;/li&gt;
&lt;li&gt;route table and route table association to associate route table with subnet&lt;/li&gt;
&lt;li&gt;EC2 instance to deploy appwrite&lt;/li&gt;
&lt;li&gt;security groups to allow inbound(&lt;code&gt;http&lt;/code&gt; , &lt;code&gt;https&lt;/code&gt;) and outbound web traffic to EC2 instance to expose appwrite&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;first, create a folder of your choice and then create &lt;code&gt;providers.tf&lt;/code&gt; file . this file will hold information about the cloud provider. next, initialize terraform project using &lt;code&gt;terraform init&lt;/code&gt; . this will allow terraform to download necessary binaries and initiate terraform.&lt;/p&gt;

&lt;h3&gt;
  
  
  providers.tf
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/aws"&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 3.27"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;required_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;= 0.14.9"&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="nx"&gt;profile&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"default"&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;after that, we need to create our first component, AWS &lt;a href="https://aws.amazon.com/vpc/" rel="noopener noreferrer"&gt;VPC&lt;/a&gt; . &lt;code&gt;VPC&lt;/code&gt; stands for Virtual Private Cloud, which is like a complete network infrastructure layer for your cloud applications. next, we need to create a subnet in our &lt;code&gt;VPC&lt;/code&gt;. this is where our EC2 instance is placed in the network.&lt;/p&gt;

&lt;p&gt;To expose this subnect to the internet we need to provision an &lt;a href="https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Internet_Gateway.html" rel="noopener noreferrer"&gt;Internet Gateway&lt;/a&gt; and also &lt;a href="https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Route_Tables.html" rel="noopener noreferrer"&gt;route table&lt;/a&gt; and &lt;a href="https://docs.aws.amazon.com/vpc/latest/userguide/WorkWithRouteTables.html#AssociateSubnet" rel="noopener noreferrer"&gt;route table associations&lt;/a&gt; to associate the route table with this subnet.&lt;/p&gt;
&lt;h3&gt;
  
  
  network.tf
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;
&lt;span class="c1"&gt;# VPC&lt;/span&gt;
&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_vpc"&lt;/span&gt; &lt;span class="s2"&gt;"vpcappwriteiacdemo"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;cidr_block&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.0.0.0/16"&lt;/span&gt;
  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"Name"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"vpc_appwriteiacdemo"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Public subnet&lt;/span&gt;
&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_subnet"&lt;/span&gt; &lt;span class="s2"&gt;"publicsubnetappwriteiacdemo"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_id&lt;/span&gt;                  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpcappwriteiacdemo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;cidr_block&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.0.1.0/24"&lt;/span&gt;
  &lt;span class="nx"&gt;map_public_ip_on_launch&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="c1"&gt;#IGW&lt;/span&gt;
&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_internet_gateway"&lt;/span&gt; &lt;span class="s2"&gt;"igwappwriteiacdemo"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpcappwriteiacdemo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"Name"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"igw_appwriteiacdemo"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_route_table"&lt;/span&gt; &lt;span class="s2"&gt;"crtpublicappwriteiacdemo"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpcappwriteiacdemo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;


  &lt;span class="nx"&gt;route&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;cidr_block&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;
    &lt;span class="c1"&gt;# directs to IGW&lt;/span&gt;
    &lt;span class="nx"&gt;gateway_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_internet_gateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;igwappwriteiacdemo&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;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"Name"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"custom_public_route_table_appwriteiacdemo"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_route_table_association"&lt;/span&gt; &lt;span class="s2"&gt;"racappwriteiacdemo"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;subnet_id&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_subnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicsubnetappwriteiacdemo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;route_table_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_route_table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;crtpublicappwriteiacdemo&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;after that, we need to specify the &lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-security-groups.html" rel="noopener noreferrer"&gt;security groups&lt;/a&gt; to allow web traffic to the EC2 instance we are provisioning. for that create &lt;code&gt;securitygroups.tf&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;here we provision 4 security groups.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;allow http ingress traffic (allow inbound http traffic to EC2) : port &lt;code&gt;80&lt;/code&gt; &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;allow https ingress traffic (allow inbound https traffic to EC2) : port &lt;code&gt;443&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;allow ssh ingress traffic (allow inbound ssh traffic to EC2) : port &lt;code&gt;22&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;allow all egress traffic (allow all outbound traffic to EC2)&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  securitygroups.tf
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_security_group"&lt;/span&gt; &lt;span class="s2"&gt;"sg_allow_http_ingress_appwriteicademo"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"sg_allow_http_ingress_appwriteicademo"&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpcappwriteiacdemo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;ingress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;description&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"http"&lt;/span&gt;
    &lt;span class="nx"&gt;from_port&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;
    &lt;span class="nx"&gt;to_port&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;
    &lt;span class="nx"&gt;protocol&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
    &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;ipv6_cidr_blocks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"::/0"&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;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_security_group"&lt;/span&gt; &lt;span class="s2"&gt;"sg_allow_https_ingress_appwriteicademo"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"sg_allow_https_ingress_appwriteicademo"&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpcappwriteiacdemo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;ingress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;description&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"http"&lt;/span&gt;
    &lt;span class="nx"&gt;from_port&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;443&lt;/span&gt;
    &lt;span class="nx"&gt;to_port&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;443&lt;/span&gt;
    &lt;span class="nx"&gt;protocol&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
    &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;ipv6_cidr_blocks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"::/0"&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;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_security_group"&lt;/span&gt; &lt;span class="s2"&gt;"sg_allow_ssh_ingress_appwriteicademo"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"sg_allow_ssh_ingress_appwriteicademo"&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpcappwriteiacdemo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;ingress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;description&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"http"&lt;/span&gt;
    &lt;span class="nx"&gt;from_port&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;
    &lt;span class="nx"&gt;to_port&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;
    &lt;span class="nx"&gt;protocol&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
    &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;ipv6_cidr_blocks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"::/0"&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;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_security_group"&lt;/span&gt; &lt;span class="s2"&gt;"sg_allow_all_egress_appwriteicademo"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"sg_allow_all_egress_appwriteicademo"&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpcappwriteiacdemo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;

  &lt;span class="nx"&gt;egress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;from_port&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="nx"&gt;to_port&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="nx"&gt;protocol&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"-1"&lt;/span&gt;
    &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;ipv6_cidr_blocks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"::/0"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;now we are almost there, just need to create our &lt;code&gt;main.tf&lt;/code&gt; file where we specify EC2 instance to provision.&lt;/p&gt;

&lt;p&gt;in this tutorial, we will provision &lt;code&gt;t2 small&lt;/code&gt; EC2 instance to the public subnet we created in our previous steps.&lt;br&gt;
I choose &lt;code&gt;t2 small&lt;/code&gt; instance type because the &lt;a href="https://appwrite.io/docs/installation#systemRequirements" rel="noopener noreferrer"&gt;minimum requirements&lt;/a&gt; to run Appwrite is 1 CPU core and 2GB of RAM which matches with &lt;a href="https://aws.amazon.com/ec2/instance-types/t2/" rel="noopener noreferrer"&gt;t2 small&lt;/a&gt; instance type.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AMIs.html" rel="noopener noreferrer"&gt;AMI&lt;/a&gt; we use here is &lt;code&gt;Ubuntu Server 20.04 LTS (HVM)&lt;/code&gt;.security groups we created earlier also have specified to our EC2 instance along with the subnet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Make sure you added your key pair name in &lt;code&gt;key_name&lt;/code&gt; value.&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  main.tf
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;
&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_instance"&lt;/span&gt; &lt;span class="s2"&gt;"appwrite-demo"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;ami&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ami-09e67e426f25ce0d7"&lt;/span&gt; &lt;span class="c1"&gt;# ubuntu 20 image&lt;/span&gt;
  &lt;span class="nx"&gt;instance_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"t2.small"&lt;/span&gt;
  &lt;span class="nx"&gt;tags&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"appwrite-ec2"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;key_name&lt;/span&gt;               &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"your-key-pair-name"&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_security_group_ids&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;aws_security_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sg_allow_all_egress_appwriteicademo&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;aws_security_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sg_allow_http_ingress_appwriteicademo&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;aws_security_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sg_allow_https_ingress_appwriteicademo&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;subnet_id&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_subnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicsubnetappwriteiacdemo&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="c1"&gt;# log public-ip after privisioning&lt;/span&gt;
&lt;span class="k"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"public-ip"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appwrite-demo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public_ip&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;


&lt;p&gt;now we have all the elements we need, let's apply this configuration using &lt;code&gt;terraform apply&lt;/code&gt; command.&lt;/p&gt;
&lt;h3&gt;
  
  
  terraform output
&lt;/h3&gt;

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

&lt;p&gt;make sure you can connect to the EC2 instance using the key pair you specified before proceeding to the next steps.&lt;/p&gt;
&lt;h3&gt;
  
  
  aws network diagram
&lt;/h3&gt;

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


&lt;h2&gt;
  
  
  Manage configurations and deploy appwrite using Ansible
&lt;/h2&gt;

&lt;p&gt;First, create an inventory file named &lt;code&gt;hosts&lt;/code&gt; and add EC2 instance public ip&lt;/p&gt;
&lt;h3&gt;
  
  
  hosts
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;your-ec2-public-ip-address
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;our newly provisioned EC2 instance has a fresh copy of ubuntu, so we need need to install &lt;a href="https://www.docker.com/" rel="noopener noreferrer"&gt;docker&lt;/a&gt; and &lt;a href="https://docs.docker.com/compose/" rel="noopener noreferrer"&gt;docker-compose&lt;/a&gt; to deploy appwrite.&lt;/p&gt;

&lt;p&gt;We can start by creating &lt;code&gt;main.yml&lt;/code&gt; file as the ansible playbook . in this file, we will have all the tasks we need to execute deploy appwrite.&lt;/p&gt;

&lt;p&gt;First, we will update apt packages, and install dependency packages for docker and docker-compose . &lt;/p&gt;

&lt;p&gt;Next we will install docker and docker-compose. &lt;/p&gt;

&lt;p&gt;Finally, we will copy the &lt;code&gt;docker-compose.yml&lt;/code&gt; and &lt;code&gt;.env&lt;/code&gt; files (which we will download in the next step) to EC2 instance deploy appwrite using docker-compose.&lt;/p&gt;
&lt;h3&gt;
  
  
  main.yml
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;become&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
  &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;all&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;deploy-appwrite&lt;/span&gt;
  &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu&lt;/span&gt;
  &lt;span class="na"&gt;tasks&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="s"&gt;Update all packages&lt;/span&gt;
      &lt;span class="na"&gt;apt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;upgrade&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dist&lt;/span&gt;
        &lt;span class="na"&gt;update_cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
        &lt;span class="na"&gt;cache_valid_time&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3600&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;Install a list of common dependancy packages&lt;/span&gt;
      &lt;span class="na"&gt;apt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;pkg&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;apt-transport-https&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ca-certificates&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;software-properties-common&lt;/span&gt;

    &lt;span class="c1"&gt;# install docker&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;docker gpg setup&lt;/span&gt;
      &lt;span class="na"&gt;apt_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://download.docker.com/linux/ubuntu/gpg&lt;/span&gt;
        &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;present&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;add docker apt repository&lt;/span&gt;
      &lt;span class="na"&gt;apt_repository&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;repo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;deb [arch=amd64] https://download.docker.com/linux/ubuntu focal stable&lt;/span&gt;
        &lt;span class="na"&gt;update_cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
        &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;present&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;Update repositories cache&lt;/span&gt;
      &lt;span class="na"&gt;apt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;update_cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&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;Install docker-ce&lt;/span&gt;
      &lt;span class="na"&gt;apt&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;docker-ce&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;add ubuntu to docker&lt;/span&gt;
      &lt;span class="na"&gt;user&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;ubuntu&lt;/span&gt;
        &lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker&lt;/span&gt;

    &lt;span class="c1"&gt;# install docker-compose&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;get platform&lt;/span&gt;
      &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;uname&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-s"&lt;/span&gt;
      &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;executable&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/bin/bash&lt;/span&gt;
      &lt;span class="na"&gt;register&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;platform&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;get architecture&lt;/span&gt;
      &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;uname&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-m"&lt;/span&gt;
      &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;executable&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/bin/bash&lt;/span&gt;
      &lt;span class="na"&gt;register&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;arch&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;install docker-compose&lt;/span&gt;
      &lt;span class="na"&gt;get_url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://github.com/docker/compose/releases/download/1.27.4/docker-compose-{{platform.stdout}}-{{arch.stdout}}&lt;/span&gt;
        &lt;span class="na"&gt;dest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/usr/local/bin/docker-compose&lt;/span&gt;
        &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;u+x,g+x"&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;Install Docker SDK for Python&lt;/span&gt;
      &lt;span class="na"&gt;apt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;pkg&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;python3&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;python3-pip&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;Update repositories cache&lt;/span&gt;
      &lt;span class="na"&gt;apt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;update_cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&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;Install Docker SDK for Python&lt;/span&gt;
      &lt;span class="na"&gt;pip&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;docker&amp;lt;5"&lt;/span&gt;
      &lt;span class="na"&gt;become&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&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;Install docker-compose SDK for Python&lt;/span&gt;
      &lt;span class="na"&gt;pip&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;docker-compose"&lt;/span&gt;
      &lt;span class="na"&gt;become&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;

    &lt;span class="c1"&gt;# deploy appwrite&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;Create appwrite directory&lt;/span&gt;
      &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/home/ubuntu/appwrite/&lt;/span&gt;
        &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;directory&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;Copy docker-compose.yml&lt;/span&gt;
      &lt;span class="na"&gt;copy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;templates/docker-compose.yml&lt;/span&gt;
        &lt;span class="na"&gt;dest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/home/ubuntu/appwrite/docker-compose.yml&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;Copy .env&lt;/span&gt;
      &lt;span class="na"&gt;copy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;templates/.env&lt;/span&gt;
        &lt;span class="na"&gt;dest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/home/ubuntu/appwrite/.env&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;docker compose up&lt;/span&gt;
      &lt;span class="na"&gt;docker_compose&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;project_src&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/home/ubuntu/appwrite&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now create &lt;code&gt;templates&lt;/code&gt; directory and add &lt;code&gt;docker-compose.yml&lt;/code&gt; and &lt;code&gt;.env&lt;/code&gt; files to &lt;code&gt;templates&lt;/code&gt; directory from this GitHub gist.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.github.com/eldadfux/977869ff6bdd7312adfd4e629ee15cc5" rel="noopener noreferrer"&gt;GitHub gist&lt;/a&gt;&lt;/p&gt;


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



&lt;p&gt;&lt;a href="https://appwrite.io/docs/installation#manual" rel="noopener noreferrer"&gt;Appwrite docs&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  final folder structure
&lt;/h3&gt;

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

&lt;p&gt;Now we have all the pieces in hand, let's run ansible playbook using the following command.&lt;/p&gt;

&lt;p&gt;In this command, we pass &lt;code&gt;main.yml&lt;/code&gt; as our playbook&lt;br&gt;
&lt;code&gt;-i&lt;/code&gt; flag to pass our inventory file &lt;code&gt;hosts&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;&lt;code&gt;--private-key&lt;/code&gt; flag to pass the key pair, to ssh EC2 instance.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ansible-playbook main.yml -i hosts --private-key [path-to-your-key]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Let's run ansible playbook and watch the magic happens 🪄.&lt;/p&gt;

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

&lt;p&gt;After successfully running ansible playbook now you can visit your EC2 public ip address and view the appwrite signup page.&lt;/p&gt;

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

&lt;p&gt;Now we can use appwrite and start building applications.&lt;/p&gt;

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



&lt;h2&gt;
  
  
  Thanks for reading till the end 🙌
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Cheers 🥂
&lt;/h2&gt;

</description>
      <category>appwrite</category>
      <category>ansible</category>
      <category>terraform</category>
    </item>
    <item>
      <title>Setting up Prometheus and Grafana on Orange Pi Zero</title>
      <dc:creator>Rajitha Gunathilake</dc:creator>
      <pubDate>Fri, 06 Aug 2021 16:28:36 +0000</pubDate>
      <link>https://forem.com/rizkyrajitha/setting-up-prometheus-and-grafana-on-orange-pi-zero-5e8n</link>
      <guid>https://forem.com/rizkyrajitha/setting-up-prometheus-and-grafana-on-orange-pi-zero-5e8n</guid>
      <description>&lt;p&gt;Hi everyone,&lt;/p&gt;

&lt;p&gt;This is a tutorial I am going to walk through how to install and configure an Orange Pi Zero with &lt;a href="https://prometheus.io/" rel="noopener noreferrer"&gt;Prometheus&lt;/a&gt;  and  &lt;a href="https://grafana.com/" rel="noopener noreferrer"&gt;Grafana&lt;/a&gt; for monitoring hardware.&lt;/p&gt;

&lt;p&gt;I am using &lt;a href="http://www.orangepi.org/orangepizero/" rel="noopener noreferrer"&gt;Orange pi zero&lt;/a&gt; in this tutorial with AllWinner H2 Quad-core processor.&lt;/p&gt;

&lt;p&gt;Let's start by updating the Orange pi.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I first installed downloaded the Prometheus from their &lt;a href="https://github.com/prometheus/prometheus/releases/tag/v2.28.1" rel="noopener noreferrer"&gt;github&lt;/a&gt; release. because my orangepi is base on arm7 architecture I downloaded the respective archive. here &lt;code&gt;-OL&lt;/code&gt; in the curl will redirect if need and save the output file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-OL&lt;/span&gt; https://github.com/prometheus/prometheus/releases/download/v2.28.1/prometheus-2.28.1.linux-armv7.tar.gz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;after downloading the image extract it to a folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xzvf&lt;/span&gt; prometheus-2.29.0-rc.0.linux-armv7.tar.gz prometheus-2.29.0-rc.0.linux-armv7/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;next, I copied the Prometheus binary to the &lt;code&gt;/usr/local/bin/&lt;/code&gt; because that I generally where your binary files live in.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;prometheus-2.29.0-rc.0.linux-armv7/
&lt;span class="nb"&gt;cp &lt;/span&gt;prometheus /usr/local/bin/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;after all that was set up, I created a service file for &lt;code&gt;systemd&lt;/code&gt; to configure Prometheus as a service in &lt;code&gt;/etc/systemd/system/&lt;/code&gt;.&lt;br&gt;
this directory is where we write &lt;code&gt;systemd&lt;/code&gt; unit files. there are many places among this one but this I a conventional directory that is best suited in this scenario.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sudo nano /etc/systemd/system/prometheus.service&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Unit]
Description=Prometheus monitoring
Wants=network-online.target
After=network-online.target

[Service]
Type=simple
ExecStart=/usr/local/bin/prometheus \
                    --config.file /home/batman/prometheus/prometheus.yml \
                    --web.route-prefix=/ \
                    --web.external-url=http://rizky.lab/prometheus
Restart=always

[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;in this file, I have added the &lt;code&gt;ExecStart&lt;/code&gt; path to the Prometheus binary. here I also added some more arguments to configure Prometheus, because I want to run Prometheus behind an Nginx reverse proxy. and also added the config file in path &lt;code&gt;/home/batman/prometheus/prometheus.yml&lt;/code&gt; so make sure you have the default &lt;code&gt;prometheus.yml&lt;/code&gt; (comes with the &lt;code&gt;tar&lt;/code&gt; archive) file in the specified path.&lt;/p&gt;



&lt;p&gt;after that is done. I restarted the systemd daemon and then enable the service.&lt;br&gt;
enabling service will make sure that this service will run after a reboot. it might do many other things behind the back but I notice this when I used this service without enabling it.&lt;br&gt;
next stared Prometheus.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl daemon-reload
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;prometheus
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart prometheus
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl status prometheus
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;next, we can start installing the &lt;a href="https://github.com/prometheus/node_exporter/releases/tag/v1.2.0" rel="noopener noreferrer"&gt;node_exporter&lt;/a&gt; for orange pi&lt;br&gt;
here also, I looked the same way, searched for the &lt;code&gt;armv7&lt;/code&gt; architecture binary.&lt;br&gt;
just as before download extract and copy it to the &lt;code&gt;/usr/local/bin/&lt;/code&gt; directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-OL&lt;/span&gt; https://github.com/prometheus/node_exporter/releases/download/v1.2.0/node_exporter-1.2.0.linux-armv7.tar.gz
&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xzvf&lt;/span&gt; node_exporter-1.2.0.linux-armv7.tar.gz node_exporter-1.2.0.linux-armv7/
&lt;span class="nb"&gt;cd &lt;/span&gt;node_exporter-1.2.0.linux-armv7/
&lt;span class="nb"&gt;sudo cp &lt;/span&gt;node_exporter  /usr/local/bin/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;next, I repeated the same steps for creating the unit file for systemd.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sudo nano /etc/systemd/system/nodeexporter.service&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Unit]
Description=node exporter for node monitoring
Wants=network-online.target
After=network-online.target

[Service]
Type=simple
ExecStart=/usr/local/bin/node_exporter
Restart=always

[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;after setting up the node_exporter , reload systemd daemon and restarted node_exporter.&lt;br&gt;
keep in mind the service will name after the name of the service file. here node_exporter service will be recognized as nodeexporter because I named the unit file &lt;code&gt;nodeexporter.service&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl daemon-reload
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;nodeexporter
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart nodeexporter
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl status nodeexporter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;now that we have all in place, next add this nodeexporter to Prometheus as a target. to do that we need to edit the&lt;br&gt;
&lt;code&gt;prometheus.yml&lt;/code&gt; file we specified earlier. I appended the following to the &lt;code&gt;prometheus.yml&lt;/code&gt;.&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="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;job_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;node_exporter"&lt;/span&gt;

  &lt;span class="na"&gt;static_configs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;targets&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;localhost:9100"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;after that restart, Prometheus to catch up with new changes. you can see the change at this point if you visit Prometheus via its opened port.&lt;/p&gt;

&lt;p&gt;after that, I set up &lt;a href="//grafana.com/"&gt;Grafana&lt;/a&gt; for the monitoring dashboard.&lt;/p&gt;

&lt;p&gt;For Grafana, it's a bit different than the last approach, hence it could be installed with apt.&lt;br&gt;
I pretty much followed they're &lt;a href="https://grafana.com/docs/grafana/latest/installation/debian/" rel="noopener noreferrer"&gt;offcial documentation&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wget &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;-O&lt;/span&gt; - https://packages.grafana.com/gpg.key | &lt;span class="nb"&gt;sudo &lt;/span&gt;apt-key add -
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"deb https://packages.grafana.com/oss/deb stable main"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; /etc/apt/sources.list.d/grafana.list
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; grafana
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl demon-reload
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;grafana-server
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start grafana-server
systemctl status grafana-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;hereafter setting up everything I need to alter come configurations in the &lt;code&gt;grafana.ini&lt;/code&gt; file to allow Grafana to access behind a reverse proxy.&lt;br&gt;
this Grafana file lives in &lt;code&gt;/etc/grafana&lt;/code&gt; directory by default.&lt;/p&gt;

&lt;p&gt;in that configuration we have to set &lt;code&gt;domain&lt;/code&gt; , &lt;code&gt;root_url&lt;/code&gt; and &lt;code&gt;serve_from_sub_path&lt;/code&gt; options.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[server]&lt;/span&gt;
&lt;span class="c"&gt;# The public facing domain name used to access grafana from a browser
&lt;/span&gt;&lt;span class="py"&gt;domain&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;rizky.lab&lt;/span&gt;

&lt;span class="c"&gt;# The full public facing url you use in browser, used for redirects and emails
# If you use reverse proxy and sub path specify full url (with sub path)
# ;root_url = %(protocol)s://%(domain)s:%(http_port)s/
&lt;/span&gt;&lt;span class="py"&gt;root_url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;%(protocol)s://%(domain)s:%(http_port)s/grafana/&lt;/span&gt;

&lt;span class="c"&gt;# Serve Grafana from subpath specified in `root_url` setting. By default it is set to `false` for compatibility reasons.
&lt;/span&gt;&lt;span class="py"&gt;serve_from_sub_path&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;true&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;after setting this up, restart Grafana to pick up with the latest changes.&lt;/p&gt;

&lt;p&gt;now as the last step configure the Nginx as the reverse proxy to access our all mighty Grafana dashboard.&lt;/p&gt;

&lt;p&gt;here I previously configured a site as &lt;code&gt;rizky.lab&lt;/code&gt; , so I am editing that to suit the needs, but you can try any way you like.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;server&lt;/span&gt; {
  &lt;span class="n"&gt;listen&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt; &lt;span class="n"&gt;default_server&lt;/span&gt;;
  &lt;span class="n"&gt;listen&lt;/span&gt; [::]:&lt;span class="m"&gt;80&lt;/span&gt; &lt;span class="n"&gt;default_server&lt;/span&gt;;

  &lt;span class="n"&gt;root&lt;/span&gt; /&lt;span class="n"&gt;var&lt;/span&gt;/&lt;span class="n"&gt;www&lt;/span&gt;/&lt;span class="n"&gt;rizky&lt;/span&gt;.&lt;span class="n"&gt;lab&lt;/span&gt;;

  &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;.&lt;span class="n"&gt;html&lt;/span&gt;;

  &lt;span class="n"&gt;server_name&lt;/span&gt; &lt;span class="n"&gt;rizky&lt;/span&gt;.&lt;span class="n"&gt;lab&lt;/span&gt;;

         &lt;span class="n"&gt;location&lt;/span&gt; /&lt;span class="n"&gt;prometheus&lt;/span&gt;/ {
                &lt;span class="n"&gt;proxy_pass&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;://&lt;span class="n"&gt;localhost&lt;/span&gt;:&lt;span class="m"&gt;9090&lt;/span&gt;/;
        }


        &lt;span class="n"&gt;location&lt;/span&gt; /&lt;span class="n"&gt;grafana&lt;/span&gt;/ {
                &lt;span class="n"&gt;proxy_pass&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;://&lt;span class="n"&gt;localhost&lt;/span&gt;:&lt;span class="m"&gt;3000&lt;/span&gt;/;
        }

        &lt;span class="n"&gt;location&lt;/span&gt; / {

                &lt;span class="n"&gt;try_files&lt;/span&gt; $&lt;span class="n"&gt;uri&lt;/span&gt; $&lt;span class="n"&gt;uri&lt;/span&gt;/ =&lt;span class="m"&gt;404&lt;/span&gt;;
        }

        &lt;span class="c"&gt;# Proxy Grafana Live WebSocket connections.
&lt;/span&gt;        &lt;span class="n"&gt;location&lt;/span&gt; /&lt;span class="n"&gt;grafana&lt;/span&gt;/&lt;span class="n"&gt;api&lt;/span&gt;/&lt;span class="n"&gt;live&lt;/span&gt; {
                &lt;span class="n"&gt;proxy_http_version&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;.&lt;span class="m"&gt;1&lt;/span&gt;;
                &lt;span class="n"&gt;proxy_set_header&lt;/span&gt; &lt;span class="n"&gt;Upgrade&lt;/span&gt; $&lt;span class="n"&gt;http_upgrade&lt;/span&gt;;
                &lt;span class="n"&gt;proxy_set_header&lt;/span&gt; &lt;span class="n"&gt;Connection&lt;/span&gt; &lt;span class="s2"&gt;"Upgrade"&lt;/span&gt;;
                &lt;span class="n"&gt;proxy_set_header&lt;/span&gt; &lt;span class="n"&gt;Host&lt;/span&gt; $&lt;span class="n"&gt;http_host&lt;/span&gt;;
                &lt;span class="n"&gt;proxy_pass&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;://&lt;span class="n"&gt;localhost&lt;/span&gt;:&lt;span class="m"&gt;3000&lt;/span&gt;/;
        }

}

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

&lt;/div&gt;



&lt;p&gt;and after all that, we can access the all mighty Grafana Dashboard, and configure it as you wish.&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%2Fuploads%2Farticles%2Fkzwobntu88og2s8axpuu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkzwobntu88og2s8axpuu.png" alt="Grafana Dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Thanks for reading till the end 🙌
&lt;/h3&gt;

&lt;h3&gt;
  
  
  Cheers 🥂
&lt;/h3&gt;

</description>
      <category>grafana</category>
      <category>prometheus</category>
      <category>devops</category>
      <category>orangepi</category>
    </item>
    <item>
      <title>Introducing LinkIn Customizable self hosted link tree app</title>
      <dc:creator>Rajitha Gunathilake</dc:creator>
      <pubDate>Sun, 20 Jun 2021 13:03:19 +0000</pubDate>
      <link>https://forem.com/rizkyrajitha/introducing-linkin-customizable-self-hosted-link-tree-platform-2255</link>
      <guid>https://forem.com/rizkyrajitha/introducing-linkin-customizable-self-hosted-link-tree-platform-2255</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbzv5h5m4zbfeldqcchrp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbzv5h5m4zbfeldqcchrp.png" alt="tile"&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%2Fuploads%2Farticles%2F0rd15xyfh4701y61jg6n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0rd15xyfh4701y61jg6n.png" alt="dashboard"&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%2Fuploads%2Farticles%2Fllj14zqnygfeytqwesy0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fllj14zqnygfeytqwesy0.png" alt="dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What is LinkIn?
&lt;/h3&gt;

&lt;p&gt;LinkIn is a self-hosted link tree app that you can customize to create your own link tree page.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why LinkIn ?
&lt;/h3&gt;

&lt;p&gt;There are many Link tree platforms available, but they do come with a price. either they include their branding or limit customization for various pricing plans. But LinkIn is totally free, you host it, you own it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can i see a demo ?
&lt;/h3&gt;

&lt;p&gt;Sure &lt;a href="http://linkindemo.vercel.app/" rel="noopener noreferrer"&gt;http://linkindemo.vercel.app/&lt;/a&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Demo username = &lt;code&gt;admin&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Demo password = &lt;code&gt;linkin123&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Is LinkIn OpenSource ?
&lt;/h3&gt;

&lt;p&gt;Absolutely yes&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&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%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/RizkyRajitha" rel="noopener noreferrer"&gt;
        RizkyRajitha
      &lt;/a&gt; / &lt;a href="https://github.com/RizkyRajitha/linkin" rel="noopener noreferrer"&gt;
        linkin
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Linkin is a customizable self hosted link tree platform.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;p&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/38534289/119221855-0522c380-bb0f-11eb-8fee-c335fd0ff67c.png"&gt;&lt;img width="400" height="400" alt="Linkin logo" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F38534289%2F119221855-0522c380-bb0f-11eb-8fee-c335fd0ff67c.png"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Linkin · &lt;a href="https://deepscan.io/dashboard#view=project&amp;amp;tid=14086&amp;amp;pid=17178&amp;amp;bid=386441" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/7cd53f67b6b58889904d387696e5c86180eb156b64c4d5601340bb1c3df24fe4/68747470733a2f2f646565707363616e2e696f2f6170692f7465616d732f31343038362f70726f6a656374732f31373137382f6272616e636865732f3338363434312f62616467652f67726164652e737667" alt="DeepScan grade"&gt;&lt;/a&gt; &lt;a href="https://codecov.io/gh/RizkyRajitha/linkin" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/ee870c6ad7154f2847d900eb8fd70ee8c1c3f2838ce9e514a152714e7ca8ac6d/68747470733a2f2f636f6465636f762e696f2f67682f52697a6b7952616a697468612f6c696e6b696e2f6272616e63682f6d61737465722f67726170682f62616467652e7376673f746f6b656e3d44504533595655595557" alt="codecov"&gt;&lt;/a&gt; &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/a0ac4580a3fb3ce0d2d8ac1f45a8fb42cfa62e88ce63345391ec1cc5e63098db/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f72697a6b7972616a697468612f6c696e6b696e3f3f7374796c653d706c6173746963"&gt;&lt;img src="https://camo.githubusercontent.com/a0ac4580a3fb3ce0d2d8ac1f45a8fb42cfa62e88ce63345391ec1cc5e63098db/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f72697a6b7972616a697468612f6c696e6b696e3f3f7374796c653d706c6173746963" alt="license"&gt;&lt;/a&gt; &lt;a href="https://github.com/RizkyRajitha/linkin/actions/workflows/coverage.yml" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/RizkyRajitha/linkin/actions/workflows/coverage.yml/badge.svg?branch=master" alt="Code Coverage"&gt;&lt;/a&gt;
&lt;/h1&gt;
&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Linkin is a customizable self-hosted link tree application.&lt;/h2&gt;
&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Free and Open Source 💯&lt;/h3&gt;
&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Self Hosted, you own your data 💽&lt;/h3&gt;

&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Customize your link tree with few clicks using a feature-rich dashboard 🤖&lt;/h3&gt;

&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;SEO friendly design built using Next js 🕸️&lt;/h3&gt;

&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Supports one-click deploy using multiple cloud providers 🚀&lt;/h3&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="http://linkindemo.vercel.app/" rel="nofollow noopener noreferrer"&gt;View Demo&lt;/a&gt;
&lt;br&gt;
&lt;a href="http://linkindemo.vercel.app/admin" rel="nofollow noopener noreferrer"&gt;Demo Admin&lt;/a&gt;
&lt;code&gt;http://linkindemo.vercel.app/admin&lt;/code&gt;
&lt;br&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Demo username = &lt;code&gt;admin&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Demo password = &lt;code&gt;linkin123&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;br&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/b12fc6f10beab9835c82983fbbeee63b098646df85d6186ef9fb22bdd051a870/68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f64696a6a716673746f2f696d6167652f75706c6f61642f76313633323933303237382f6c696e6b696e2f6c696e6b696e5f79726772336b2e676966"&gt;&lt;img src="https://camo.githubusercontent.com/b12fc6f10beab9835c82983fbbeee63b098646df85d6186ef9fb22bdd051a870/68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f64696a6a716673746f2f696d6167652f75706c6f61642f76313633323933303237382f6c696e6b696e2f6c696e6b696e5f79726772336b2e676966" alt="linkin gif" width="100%"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;br&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Deploy with Vercel&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a href="https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FRizkyRajitha%2Flinkin&amp;amp;env=DATABASE_URL%2CHASHSALT%2CNODE_ENV" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/20bea215d35a4e28f2c92ea5b657d006b087687486858a40de2922a4636301ab/68747470733a2f2f76657263656c2e636f6d2f627574746f6e" alt="Deploy with Vercel"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Deploy with Heroku&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a href="https://heroku.com/deploy?template=https://github.com/RizkyRajitha/linkin" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/dc2056acd0e6ff421bfc2b129417f4f832d626c61d1c083221211d8503a429f7/68747470733a2f2f7777772e6865726f6b7563646e2e636f6d2f6465706c6f792f627574746f6e2e737667" alt="Deploy"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Deploy with Railway&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a href="https://railway.app/new/template?template=https%3A%2F%2Fgithub.com%2FRizkyRajitha%2Flinkin&amp;amp;plugins=postgresql&amp;amp;envs=HASHSALT%2CPORT%2CRAILWAY&amp;amp;HASHSALTDesc=Random+secret+HASHSALT+for+JWT+and+password+encryption&amp;amp;PORTDesc=Exposed+Port+in+Dockerfile&amp;amp;RAILWAYDesc=migrate+and+seed+the+database+in+railway+.+done+in+docker+image+build+time&amp;amp;PORTDefault=3000&amp;amp;RAILWAYDefault=1" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/e4002051668809c220b10ad92ddd6fb87f365d8cd4ff470e0aeca3bc5b05450e/68747470733a2f2f7261696c7761792e6170702f627574746f6e2e737667" alt="Deploy on Railway"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/38534289/119221911-4ca94f80-bb0f-11eb-94ff-31f1c3a51d06.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F38534289%2F119221911-4ca94f80-bb0f-11eb-94ff-31f1c3a51d06.png" alt="Screenshot_2021-05-22 LinkIn's Link tree Page"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/38534289/119221942-7d898480-bb0f-11eb-9175-5e139fa57f0a.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F38534289%2F119221942-7d898480-bb0f-11eb-9175-5e139fa57f0a.png" alt="Screenshot_2021-05-22 Linkin Dashboard"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/38534289/119221939-7c585780-bb0f-11eb-944f-514beb5573b7.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F38534289%2F119221939-7c585780-bb0f-11eb-944f-514beb5573b7.png" alt="Screenshot_2021-05-22 Linkin Dashboard"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Getting started&lt;/h2&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Deploy in Vercel
&lt;ul&gt;
&lt;li&gt;set environment variables
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;DATABASE_URL&lt;/code&gt; - &lt;strong&gt;Postgres&lt;/strong&gt; database url&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;HASHSALT&lt;/code&gt; - random secret key&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;NODE_ENV&lt;/code&gt; - set NODE_ENV to &lt;code&gt;production&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;after successfully deploying visit &lt;code&gt;youdomain/admin&lt;/code&gt; to view admin login&lt;/li&gt;
&lt;li&gt;use default login credentials
&lt;ul&gt;
&lt;li&gt;username = &lt;code&gt;admin&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;password = &lt;code&gt;linkin123&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;after a successfull login you will be able to see above admin dashboard.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;br&gt;
&lt;br&gt;
&lt;ul&gt;
&lt;li&gt;Deploy in Heroku
&lt;ul&gt;
&lt;li&gt;set environment variables
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;DATABASE_URL&lt;/code&gt; - &lt;strong&gt;Postgres&lt;/strong&gt; database url&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;HASHSALT&lt;/code&gt; - random secret key&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;after successfully deploying visit &lt;code&gt;youdomain/admin&lt;/code&gt; to…&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/RizkyRajitha/linkin" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h3&gt;
  
  
  what LinkIn is made of ?
&lt;/h3&gt;

&lt;p&gt;linkin is made using &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt; , and &lt;a href="https://www.postgresql.org/" rel="noopener noreferrer"&gt;Postgres&lt;/a&gt; as the database. &lt;a href="https://getbootstrap.com/" rel="noopener noreferrer"&gt;Bootstrap 5&lt;/a&gt; for UI components.&lt;/p&gt;

&lt;h3&gt;
  
  
  how to get my own LinkIn page ?
&lt;/h3&gt;

&lt;p&gt;visit &lt;a href="https://github.com/RizkyRajitha/linkin" rel="noopener noreferrer"&gt;LinkIn GitHub page&lt;/a&gt; and use the one-click deploy buttons to spin your own LinkIn page.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&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%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/RizkyRajitha" rel="noopener noreferrer"&gt;
        RizkyRajitha
      &lt;/a&gt; / &lt;a href="https://github.com/RizkyRajitha/linkin" rel="noopener noreferrer"&gt;
        linkin
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Linkin is a customizable self hosted link tree platform.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;p&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/38534289/119221855-0522c380-bb0f-11eb-8fee-c335fd0ff67c.png"&gt;&lt;img width="400" height="400" alt="Linkin logo" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F38534289%2F119221855-0522c380-bb0f-11eb-8fee-c335fd0ff67c.png"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Linkin · &lt;a href="https://deepscan.io/dashboard#view=project&amp;amp;tid=14086&amp;amp;pid=17178&amp;amp;bid=386441" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/7cd53f67b6b58889904d387696e5c86180eb156b64c4d5601340bb1c3df24fe4/68747470733a2f2f646565707363616e2e696f2f6170692f7465616d732f31343038362f70726f6a656374732f31373137382f6272616e636865732f3338363434312f62616467652f67726164652e737667" alt="DeepScan grade"&gt;&lt;/a&gt; &lt;a href="https://codecov.io/gh/RizkyRajitha/linkin" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/ee870c6ad7154f2847d900eb8fd70ee8c1c3f2838ce9e514a152714e7ca8ac6d/68747470733a2f2f636f6465636f762e696f2f67682f52697a6b7952616a697468612f6c696e6b696e2f6272616e63682f6d61737465722f67726170682f62616467652e7376673f746f6b656e3d44504533595655595557" alt="codecov"&gt;&lt;/a&gt; &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/a0ac4580a3fb3ce0d2d8ac1f45a8fb42cfa62e88ce63345391ec1cc5e63098db/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f72697a6b7972616a697468612f6c696e6b696e3f3f7374796c653d706c6173746963"&gt;&lt;img src="https://camo.githubusercontent.com/a0ac4580a3fb3ce0d2d8ac1f45a8fb42cfa62e88ce63345391ec1cc5e63098db/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f72697a6b7972616a697468612f6c696e6b696e3f3f7374796c653d706c6173746963" alt="license"&gt;&lt;/a&gt; &lt;a href="https://github.com/RizkyRajitha/linkin/actions/workflows/coverage.yml" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/RizkyRajitha/linkin/actions/workflows/coverage.yml/badge.svg?branch=master" alt="Code Coverage"&gt;&lt;/a&gt;
&lt;/h1&gt;
&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Linkin is a customizable self-hosted link tree application.&lt;/h2&gt;
&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Free and Open Source 💯&lt;/h3&gt;
&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Self Hosted, you own your data 💽&lt;/h3&gt;

&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Customize your link tree with few clicks using a feature-rich dashboard 🤖&lt;/h3&gt;

&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;SEO friendly design built using Next js 🕸️&lt;/h3&gt;

&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Supports one-click deploy using multiple cloud providers 🚀&lt;/h3&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="http://linkindemo.vercel.app/" rel="nofollow noopener noreferrer"&gt;View Demo&lt;/a&gt;
&lt;br&gt;
&lt;a href="http://linkindemo.vercel.app/admin" rel="nofollow noopener noreferrer"&gt;Demo Admin&lt;/a&gt;
&lt;code&gt;http://linkindemo.vercel.app/admin&lt;/code&gt;
&lt;br&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Demo username = &lt;code&gt;admin&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Demo password = &lt;code&gt;linkin123&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;br&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/b12fc6f10beab9835c82983fbbeee63b098646df85d6186ef9fb22bdd051a870/68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f64696a6a716673746f2f696d6167652f75706c6f61642f76313633323933303237382f6c696e6b696e2f6c696e6b696e5f79726772336b2e676966"&gt;&lt;img src="https://camo.githubusercontent.com/b12fc6f10beab9835c82983fbbeee63b098646df85d6186ef9fb22bdd051a870/68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f64696a6a716673746f2f696d6167652f75706c6f61642f76313633323933303237382f6c696e6b696e2f6c696e6b696e5f79726772336b2e676966" alt="linkin gif" width="100%"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;br&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Deploy with Vercel&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a href="https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FRizkyRajitha%2Flinkin&amp;amp;env=DATABASE_URL%2CHASHSALT%2CNODE_ENV" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/20bea215d35a4e28f2c92ea5b657d006b087687486858a40de2922a4636301ab/68747470733a2f2f76657263656c2e636f6d2f627574746f6e" alt="Deploy with Vercel"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Deploy with Heroku&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a href="https://heroku.com/deploy?template=https://github.com/RizkyRajitha/linkin" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/dc2056acd0e6ff421bfc2b129417f4f832d626c61d1c083221211d8503a429f7/68747470733a2f2f7777772e6865726f6b7563646e2e636f6d2f6465706c6f792f627574746f6e2e737667" alt="Deploy"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Deploy with Railway&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a href="https://railway.app/new/template?template=https%3A%2F%2Fgithub.com%2FRizkyRajitha%2Flinkin&amp;amp;plugins=postgresql&amp;amp;envs=HASHSALT%2CPORT%2CRAILWAY&amp;amp;HASHSALTDesc=Random+secret+HASHSALT+for+JWT+and+password+encryption&amp;amp;PORTDesc=Exposed+Port+in+Dockerfile&amp;amp;RAILWAYDesc=migrate+and+seed+the+database+in+railway+.+done+in+docker+image+build+time&amp;amp;PORTDefault=3000&amp;amp;RAILWAYDefault=1" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/e4002051668809c220b10ad92ddd6fb87f365d8cd4ff470e0aeca3bc5b05450e/68747470733a2f2f7261696c7761792e6170702f627574746f6e2e737667" alt="Deploy on Railway"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/38534289/119221911-4ca94f80-bb0f-11eb-94ff-31f1c3a51d06.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F38534289%2F119221911-4ca94f80-bb0f-11eb-94ff-31f1c3a51d06.png" alt="Screenshot_2021-05-22 LinkIn's Link tree Page"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/38534289/119221942-7d898480-bb0f-11eb-9175-5e139fa57f0a.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F38534289%2F119221942-7d898480-bb0f-11eb-9175-5e139fa57f0a.png" alt="Screenshot_2021-05-22 Linkin Dashboard"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/38534289/119221939-7c585780-bb0f-11eb-944f-514beb5573b7.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F38534289%2F119221939-7c585780-bb0f-11eb-944f-514beb5573b7.png" alt="Screenshot_2021-05-22 Linkin Dashboard"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Getting started&lt;/h2&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Deploy in Vercel
&lt;ul&gt;
&lt;li&gt;set environment variables
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;DATABASE_URL&lt;/code&gt; - &lt;strong&gt;Postgres&lt;/strong&gt; database url&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;HASHSALT&lt;/code&gt; - random secret key&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;NODE_ENV&lt;/code&gt; - set NODE_ENV to &lt;code&gt;production&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;after successfully deploying visit &lt;code&gt;youdomain/admin&lt;/code&gt; to view admin login&lt;/li&gt;
&lt;li&gt;use default login credentials
&lt;ul&gt;
&lt;li&gt;username = &lt;code&gt;admin&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;password = &lt;code&gt;linkin123&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;after a successfull login you will be able to see above admin dashboard.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;br&gt;
&lt;br&gt;
&lt;ul&gt;
&lt;li&gt;Deploy in Heroku
&lt;ul&gt;
&lt;li&gt;set environment variables
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;DATABASE_URL&lt;/code&gt; - &lt;strong&gt;Postgres&lt;/strong&gt; database url&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;HASHSALT&lt;/code&gt; - random secret key&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;after successfully deploying visit &lt;code&gt;youdomain/admin&lt;/code&gt; to…&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/RizkyRajitha/linkin" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;linkin currently supports hosting in &lt;a href="https://vercel.com/" rel="noopener noreferrer"&gt;vercel&lt;/a&gt; and &lt;a href="https://www.heroku.com/" rel="noopener noreferrer"&gt;heroku&lt;/a&gt; platforms. you have to connect LinkIn with your own Postgres database instance and start making your link tree. LinkIn currently supports fonts from a external url (example - &lt;a href="https://fonts.google.com/" rel="noopener noreferrer"&gt;google fonts&lt;/a&gt;) , icons from &lt;a href="https://fontawesome.com/" rel="noopener noreferrer"&gt;fontawesome&lt;/a&gt; , and avatar image from external url (example - &lt;a href="https://cloudinary.com/" rel="noopener noreferrer"&gt;cloudinary&lt;/a&gt; ) . &lt;/p&gt;

&lt;h3&gt;
  
  
  I have some experience in web dev. can I customize it on my own ?
&lt;/h3&gt;

&lt;p&gt;Yes. LinkIn has a single component for the link tree. so you can add your own styles that LinkIn doesn't support and create your very own link tree.&lt;/p&gt;

&lt;p&gt;LinkIn is in pretty early stages so you might find some hiccups along the way .you can visit &lt;a href="https://github.com/RizkyRajitha/linkin" rel="noopener noreferrer"&gt;linkin Github repo&lt;/a&gt; and create issues. &lt;/p&gt;

&lt;h3&gt;
  
  
  What do you think about Linkin? Share em in the comment section.
&lt;/h3&gt;



&lt;h3&gt;
  
  
  cheers 🥂
&lt;/h3&gt;

</description>
      <category>showdev</category>
      <category>nextjs</category>
      <category>react</category>
      <category>selfhosted</category>
    </item>
    <item>
      <title>Connect API and a Database with Docker Network</title>
      <dc:creator>Rajitha Gunathilake</dc:creator>
      <pubDate>Wed, 02 Jun 2021 09:58:37 +0000</pubDate>
      <link>https://forem.com/rizkyrajitha/connect-api-and-a-database-with-docker-network-299g</link>
      <guid>https://forem.com/rizkyrajitha/connect-api-and-a-database-with-docker-network-299g</guid>
      <description>&lt;p&gt;Hi everyone,&lt;/p&gt;

&lt;p&gt;This is a tutorial I am going to walk through how to run two docker containers, add them to a new docker network and communicate with each other.&lt;/p&gt;

&lt;p&gt;for this, I am going to create a demo API with nodejs and a Postgres database in containers, and connect them to a docker network.  &lt;/p&gt;

&lt;p&gt;so first I am creating a docker network. docker network is an isolated network layer, which allows you to add containers to it. we can assign these containers' IP addresses inside the network, and let them communicate. host to container port mapping is still available when using a network in a container. docker will create a &lt;code&gt;bridge&lt;/code&gt; by default. &lt;/p&gt;

&lt;p&gt;to create a docker network &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

docker network create &lt;span class="nt"&gt;--subnet&lt;/span&gt; 172.20.0.0/16 dockernetworkdemo


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

&lt;/div&gt;
&lt;p&gt;here I have specified a &lt;code&gt;subnet&lt;/code&gt; with the value &lt;code&gt;172.20.0.0/16&lt;/code&gt; in CIDR format. &lt;/p&gt;

&lt;p&gt;after we run this command we can check it using &lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

docker network &lt;span class="nb"&gt;ls&lt;/span&gt;


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

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

&lt;p&gt;we can also use &lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

docker network inspect dockernetworkdemo


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

&lt;/div&gt;
&lt;p&gt;to view more details about the network. &lt;/p&gt;

&lt;p&gt;here &lt;code&gt;dockernetworkdemo&lt;/code&gt; is the name of the network we specified then creating the network.&lt;/p&gt;

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

&lt;p&gt;now we have the network in place next move to the database. &lt;br&gt;
here we are going to use a Postgres database. &lt;/p&gt;

&lt;p&gt;to spin a Postgres database I will use &lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

docker run &lt;span class="nt"&gt;--name&lt;/span&gt; dockernetworkdemopg &lt;span class="nt"&gt;-p&lt;/span&gt; 6543:5432 &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;123 &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;POSTGRES_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;postgres &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;POSTGRES_DB&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;postgres &lt;span class="nt"&gt;-d&lt;/span&gt; postgres:10-alpine


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

&lt;/div&gt;
&lt;p&gt;in this command,&lt;/p&gt;

&lt;p&gt;&lt;code&gt;dockernetworkdemopg&lt;/code&gt; is the name of the container &lt;/p&gt;

&lt;p&gt;&lt;code&gt;-p 6543:5432&lt;/code&gt; will map port &lt;code&gt;5432&lt;/code&gt; in the container to &lt;code&gt;6543&lt;/code&gt; host port. here I have used a different port because I have a Postgres database already running on port &lt;code&gt;5432&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;-e POSTGRES_PASSWORD=123 -e POSTGRES_USER=postgres -e POSTGRES_DB=postgres&lt;/code&gt; here &lt;code&gt;-e&lt;/code&gt; flag will pass an environment variable to the container, here we specify the password, user, and database. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;-d&lt;/code&gt; flag will run this container in detached mode. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;postgres:10-alpine&lt;/code&gt; is the name and the tag of the Postgres image we are fetching from the &lt;a href="https://hub.docker.com/" rel="noopener noreferrer"&gt;Docker Hub&lt;/a&gt;. here I have chosen the alpine version because it is lightweight and smaller in size around 20 mb . &lt;/p&gt;

&lt;p&gt;now if we use &lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

docker ps 


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

&lt;/div&gt;
&lt;p&gt;command we can see that our Postgres container is up and running.&lt;/p&gt;

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

&lt;p&gt;next, we are going to connect the Postgres container (&lt;code&gt;dockernetworkdemopg&lt;/code&gt;) to the network (&lt;code&gt;dockernetworkdemo&lt;/code&gt;) we created.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

docker network connect &lt;span class="nt"&gt;--ip&lt;/span&gt; 172.20.0.5 dockernetworkdemo dockernetworkdemopg



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

&lt;/div&gt;
&lt;p&gt;here we have specified the IP of the connecting container to be &lt;code&gt;172.20.0.5&lt;/code&gt; using &lt;code&gt;--ip&lt;/code&gt; flag.&lt;/p&gt;

&lt;p&gt;now if we run the network inspect command we can see &lt;br&gt;
the container we added. &lt;/p&gt;

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

&lt;p&gt;now we'll setup the API. I am using a simple nodejs API, and build a docker image.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&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%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/RizkyRajitha" rel="noopener noreferrer"&gt;
        RizkyRajitha
      &lt;/a&gt; / &lt;a href="https://github.com/RizkyRajitha/dockernetworkdemo" rel="noopener noreferrer"&gt;
        dockernetworkdemo
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="MD"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Connect API and a Database with Docker Network&lt;/h1&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/RizkyRajitha/dockernetworkdemoAPI.jpg"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FRizkyRajitha%2FdockernetworkdemoAPI.jpg" alt="network diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;/div&gt;
&lt;br&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/RizkyRajitha/dockernetworkdemo" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="s"&gt;FROM  node:current-alpine3.12&lt;/span&gt;

&lt;span class="s"&gt;WORKDIR /usr/src/app&lt;/span&gt;

&lt;span class="s"&gt;COPY package*.json ./&lt;/span&gt;

&lt;span class="s"&gt;RUN npm i&lt;/span&gt;

&lt;span class="s"&gt;ENV NODE_ENV=production&lt;/span&gt;
&lt;span class="s"&gt;ENV DBURL=postgres://postgres:123@172.20.0.5:5432/postgres&lt;/span&gt;
&lt;span class="s"&gt;ENV PORT=3001&lt;/span&gt;

&lt;span class="s"&gt;COPY . .&lt;/span&gt;

&lt;span class="s"&gt;EXPOSE &lt;/span&gt;&lt;span class="m"&gt;3001&lt;/span&gt;

&lt;span class="s"&gt;CMD [ "npm", "run" , "prod" ]&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;in the dockerfile, we have specified the environment variable &lt;code&gt;DBURL&lt;/code&gt; to the Postgres container we ran earlier.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ENV DBURL=postgres://postgres:123@172.20.0.5:5432/postgres&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;in the connection string &lt;code&gt;postgres://postgres:123@172.20.0.5:5432/postgres&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;172.20.0.5:5432&lt;/code&gt; is the IP address and the port of the Postgres container.&lt;code&gt;172.20.0.5&lt;/code&gt; is the IP address we specified when connecting to the network. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

docker build &lt;span class="nt"&gt;-t&lt;/span&gt; dockernetworkdemoapi &lt;span class="nb"&gt;.&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;here &lt;code&gt;-t&lt;/code&gt; will set the name and tag to &lt;code&gt;dockernetworkdemoapi:latest&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;before running the images we have to create the database relation so that we can query data using the API . for that I am using a migration script to run some SQL 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%2Fuploads%2Farticles%2F82ylkhky01ow4uht5a8n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F82ylkhky01ow4uht5a8n.png" alt="migrate"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;because migration is running in the host machine, connection string is passed as &lt;code&gt;postgres://postgres:123@localhost:6543/postgres&lt;/code&gt; with database IP as &lt;code&gt;localhost&lt;/code&gt; and port as &lt;code&gt;6543&lt;/code&gt; specified when running the Postgres database.&lt;/p&gt;

&lt;p&gt;next, we will run the API image &lt;code&gt;dockernetworkdemoapi:latest&lt;/code&gt; . &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

docker run &lt;span class="nt"&gt;--name&lt;/span&gt; dockernetworkdemoapicont &lt;span class="nt"&gt;-p&lt;/span&gt; 3001:3001 dockernetworkdemoapi:latest


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

&lt;/div&gt;

&lt;p&gt;in the above command &lt;code&gt;--name&lt;/code&gt; flag specifies the name of the container to be &lt;code&gt;dockernetworkdemoapicont&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;-p 3001:3001&lt;/code&gt; to publish the ports with &lt;code&gt;3001&lt;/code&gt; container port to &lt;code&gt;3001&lt;/code&gt; host port. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;dockernetworkdemoapi:latest&lt;/code&gt; is the docker image. &lt;/p&gt;

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

&lt;p&gt;here i ran the container without detached mode so we can see the output.&lt;/p&gt;

&lt;p&gt;now as the final step we are connecting the API (&lt;code&gt;dockernetworkdemoapicont&lt;/code&gt;) container to the &lt;code&gt;dockernetworkdemo&lt;/code&gt; docker network.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

docker network connect dockernetworkdemo dockernetworkdemoapicont


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

&lt;/div&gt;

&lt;p&gt;now if we send a get request to the API we can see that API is able to connect to the database.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fobterhot53q4rflpd7qt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fobterhot53q4rflpd7qt.png" alt="api request"&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%2Fuploads%2Farticles%2Fa4tsmx0mpq5waxs71lr0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa4tsmx0mpq5waxs71lr0.png" alt="api log"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;final docker inspect output&lt;/p&gt;

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

&lt;p&gt;illustration of the network.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Thanks for reading till the end 🙌
&lt;/h2&gt;

&lt;h2&gt;
  
  
  I value your opinions, and appreciate you taking the time to share your thoughts.
&lt;/h2&gt;

</description>
      <category>docker</category>
      <category>node</category>
      <category>postgres</category>
      <category>devops</category>
    </item>
    <item>
      <title>Spinning service containers in GitHub action workflow</title>
      <dc:creator>Rajitha Gunathilake</dc:creator>
      <pubDate>Sat, 29 May 2021 17:03:25 +0000</pubDate>
      <link>https://forem.com/rizkyrajitha/spinning-service-containers-in-github-action-workflow-56g0</link>
      <guid>https://forem.com/rizkyrajitha/spinning-service-containers-in-github-action-workflow-56g0</guid>
      <description>&lt;p&gt;Hi everyone,&lt;/p&gt;

&lt;p&gt;This is a tutorial I am going to walk through how to run docker containers in &lt;a href="https://docs.github.com/en/actions" rel="noopener noreferrer"&gt;GitHub Action&lt;/a&gt; workflows.&lt;br&gt;
This task is often useful in testing workflows where we need a temporary database to run test cases. we will run a &lt;a href="https://www.postgresql.org/" rel="noopener noreferrer"&gt;PostgreSQL&lt;/a&gt; database in this tutorial.&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Running Tests&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;

        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
          &lt;span class="na"&gt;POSTGRES_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
          &lt;span class="na"&gt;POSTGRES_DB&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;

        &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;-&lt;/span&gt;
          &lt;span class="s"&gt;--health-cmd pg_isready&lt;/span&gt;
          &lt;span class="s"&gt;--health-interval 10s&lt;/span&gt;
          &lt;span class="s"&gt;--health-timeout 5s&lt;/span&gt;
          &lt;span class="s"&gt;--health-retries 5&lt;/span&gt;
        &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;5432:5432&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;in the above workflow snippet, we do several things. &lt;/p&gt;

&lt;p&gt;1.&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;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;br&gt;
 will set when to run this workflow. here we have specified &lt;code&gt;push&lt;/code&gt;,  so on every &lt;code&gt;push&lt;/code&gt; event, this workflow will run. keep in mind that this is an array so we can use multiple options like &lt;code&gt;pull_request&lt;/code&gt; etc.&lt;/p&gt;

&lt;p&gt;2.&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;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;service-container-demo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;this code snippet will specify our job . &lt;code&gt;service-container-demo&lt;/code&gt; is the name of our job. &lt;/p&gt;

&lt;p&gt;3.&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;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;br&gt;
 specifies, on what operating system we need to run this job. here we have specified to run it on ubuntu's latest operating system.&lt;/p&gt;

&lt;p&gt;4.&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;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;here we specify the services we need to run, and the name of the service container. name of the service container is used to access it. &lt;/p&gt;

&lt;p&gt;5.&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;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;br&gt;
 this line will specify what image we need to run. these docker images are fetched from &lt;a href="https://hub.docker.com/" rel="noopener noreferrer"&gt;Docker Hub&lt;/a&gt; . so we need to specify the respective image name from Docker Hub. &lt;/p&gt;

&lt;p&gt;6.&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;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_DB&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;here we have specified the environment variables we need to provide to the service container. we have provided the password, username, and the default database here, so we can later access the database via those credentials. &lt;/p&gt;

&lt;p&gt;7.&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;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;-&lt;/span&gt;
    &lt;span class="s"&gt;--health-cmd pg_isready&lt;/span&gt;
    &lt;span class="s"&gt;--health-interval 10s&lt;/span&gt;
    &lt;span class="s"&gt;--health-timeout 5s&lt;/span&gt;
    &lt;span class="s"&gt;--health-retries 5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;we have to wait some time till the container is up and running .so to check that we have specified options to periodically check and after it is started proceed to the next steps. &lt;/p&gt;

&lt;p&gt;8.&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;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
   &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;5432:5432&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;here we have specified the port mapping. &lt;code&gt;5432&lt;/code&gt; host port &lt;br&gt;
is mapped to &lt;code&gt;5432&lt;/code&gt; container port.&lt;/p&gt;

&lt;p&gt;next, we can define the steps of the workflow. here we can migrate to the service container database,  and run the required tests.&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="c1"&gt;# define steps of workflow&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# run steps required in the workflow&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;Checkout repository&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&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;Set up Node.js 14.x&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;14.x&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;Install dependencies&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm install&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;migrate database&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node migrate.js&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;DBURL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;postgres://postgres:postgres@localhost:5432/postgres"&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;Run the mock tests function&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node getuser.js&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;DBURL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres://postgres:postgres@localhost:5432/postgres&lt;/span&gt;

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

&lt;/div&gt;


&lt;p&gt;in above snippet  &lt;code&gt;DBURL&lt;/code&gt; is set to  &lt;code&gt;postgres://postgres:postgres@localhost:5432/postgres&lt;/code&gt; . it is derived through the configurations we set in the service container earlier. the host is provided as &lt;code&gt;localhost&lt;/code&gt; because the docker and host machine both runs on &lt;code&gt;localhost&lt;/code&gt;. and here, we are passing it to the step,  through an environment variable.&lt;/p&gt;
&lt;h4&gt;
  
  
  database migration
&lt;/h4&gt;

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

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

&lt;p&gt;complete workflow file .&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;example GitHub repo&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&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%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/RizkyRajitha" rel="noopener noreferrer"&gt;
        RizkyRajitha
      &lt;/a&gt; / &lt;a href="https://github.com/RizkyRajitha/gawscontainerdemo" rel="noopener noreferrer"&gt;
        gawscontainerdemo
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;GitHub action workflow with service containers&lt;/h2&gt;

&lt;/div&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/RizkyRajitha/gawscontainerdemo" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;h3&gt;
  
  
  Thanks for reading till the end 🙌
&lt;/h3&gt;

&lt;h3&gt;
  
  
  I value your opinions, and appreciate you taking the time to share your thoughts.
&lt;/h3&gt;

</description>
      <category>postgres</category>
      <category>docker</category>
      <category>devops</category>
    </item>
    <item>
      <title>Sending images and more with Telegram bot</title>
      <dc:creator>Rajitha Gunathilake</dc:creator>
      <pubDate>Mon, 24 May 2021 17:05:45 +0000</pubDate>
      <link>https://forem.com/rizkyrajitha/sending-images-and-more-with-telegram-bot-4c0h</link>
      <guid>https://forem.com/rizkyrajitha/sending-images-and-more-with-telegram-bot-4c0h</guid>
      <description>&lt;p&gt;Hi everyone,&lt;/p&gt;

&lt;p&gt;This is a follow-up tutorial on my previous tutorial about Sending messages with Telegram bot. make sure you check that before following this tutorial. &lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/rizkyrajitha" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F247664%2Fbb29688b-6d69-444c-b5fb-d5b4365bb671.jpeg" alt="rizkyrajitha"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/rizkyrajitha/get-notifications-with-telegram-bot-537l" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Sending messages with Telegram bot&lt;/h2&gt;
      &lt;h3&gt;Rajitha Gunathilake ・ Nov 10 '19&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#bots&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#telegram&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#http&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;in the last tutorial, we talked about sending messages, but telegram has a powerful API that allows us to send more than just text messages.&lt;br&gt;
so in this tutorial, we will get to know how to send images with a telegram bot.This procedure is really simple. we need to send a post request to telegram API with our photo as &lt;code&gt;multipart/form-data&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is similar to what we used previously, the change is now we are using a different route, and sending form-data with our image attached&lt;/p&gt;

&lt;p&gt;Using the follwoing API endpoint &lt;code&gt;https://api.telegram.org/bot&amp;lt;token&amp;gt;/sendPhoto?chat_id=&amp;lt;group chat id &amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;in &lt;code&gt;multipart form data&lt;/code&gt; attach an image file with the name &lt;code&gt;photo&lt;/code&gt;&lt;/p&gt;

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

&lt;p&gt;and after sending this we can get a response with &lt;code&gt;"ok": true,&lt;/code&gt; and if we look at the chat, we can see that the photo is received. &lt;/p&gt;

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

&lt;p&gt;This can also be done programmatically, I will use nodejs to demonstrate in this example. &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="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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;fs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fs&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;FormData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;form-data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;readStream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createReadStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./image.png&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FormData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;photo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;readStream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s2"&gt;`https://api.telegram.org/bot&amp;lt;token&amp;gt;/sendPhoto?chat_id=-&amp;lt;chat id&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;form&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="nf"&gt;then&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="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="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;error&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&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="p"&gt;});&lt;/span&gt;



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

&lt;/div&gt;

&lt;p&gt;we have used &lt;code&gt;node-fetch&lt;/code&gt; package to send the HTTP request from nodejs and &lt;code&gt;form-data&lt;/code&gt; package to append formdata to the request. &lt;/p&gt;

&lt;p&gt;and after running the script we can see that we get &lt;code&gt;"ok": true,&lt;/code&gt; just like before. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdwy5si27likv8ngpi4ut.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdwy5si27likv8ngpi4ut.png" alt="bash output"&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%2Fuploads%2Farticles%2Fgn77i05fpfti78fk6ds8.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgn77i05fpfti78fk6ds8.jpeg" alt="chat image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;there are some limits provided by the telegram when using sendPhoto API &lt;br&gt;
"The photo must be at most &lt;strong&gt;10 MB&lt;/strong&gt; in size. The photo's width and height must not exceed &lt;strong&gt;10000&lt;/strong&gt; in total. Width and height ratio must be at most 20".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://core.telegram.org/bots/api#sendphoto" rel="noopener noreferrer"&gt;Telegram API reference sendphoto method&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;we can use &lt;code&gt;sendAudio&lt;/code&gt; just like the last example to send audio files. &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="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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;fs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fs&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;FormData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;form-data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;readStream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createReadStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./audio.mp3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FormData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;audio&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;readStream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;title&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;audio dev test&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="c1"&gt;// to show as the title in chat&lt;/span&gt;

&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s2"&gt;`https://api.telegram.org/bot&amp;lt;token&amp;gt;/sendAudio?chat_id=-&amp;lt;chat id&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;form&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="nf"&gt;then&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="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="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;error&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&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="p"&gt;});&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;limits provided by the telegram when using &lt;code&gt;sendAudio&lt;/code&gt; API &lt;/p&gt;

&lt;p&gt;"Your audio must be in the &lt;strong&gt;.MP3&lt;/strong&gt; or &lt;strong&gt;.M4A&lt;/strong&gt; format. On success, the sent Message is returned. Bots can currently send audio files of up to &lt;strong&gt;50 MB&lt;/strong&gt; in size, this limit may be changed in the future."&lt;/p&gt;

&lt;p&gt;&lt;a href="https://core.telegram.org/bots/api#sendaudio" rel="noopener noreferrer"&gt;Telegram API reference sendaudio method&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%2Fuploads%2Farticles%2Fu7hwg4fanslwnmw2j7yg.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu7hwg4fanslwnmw2j7yg.jpeg" alt="audio chat"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And the list goes on and on for documents, voice messages,animations , videos  etc. you can find all the provided methods in the &lt;a href="https://core.telegram.org/bots/api" rel="noopener noreferrer"&gt;Telegram reference&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Thanks for reading till the end 🙌
&lt;/h2&gt;

&lt;h2&gt;
  
  
  share your thoughts in the comments section.
&lt;/h2&gt;

</description>
      <category>bots</category>
      <category>telegram</category>
      <category>http</category>
    </item>
    <item>
      <title>popular games and some facts about them</title>
      <dc:creator>Rajitha Gunathilake</dc:creator>
      <pubDate>Wed, 10 Mar 2021 09:10:39 +0000</pubDate>
      <link>https://forem.com/rizkyrajitha/popular-games-and-their-some-facts-about-them-56od</link>
      <guid>https://forem.com/rizkyrajitha/popular-games-and-their-some-facts-about-them-56od</guid>
      <description>&lt;p&gt;Hi everyone,&lt;/p&gt;

&lt;p&gt;In this blog post, I will list some popular games and some details about them. so without further ado let's start.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. &lt;a href="https://www.rockstargames.com/V/" rel="noopener noreferrer"&gt;Grand Theft Auto V&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzblz5er0qsmdwl7xb4ic.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzblz5er0qsmdwl7xb4ic.jpg" alt="Grand Theft Auto V"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Game engine - &lt;a href="https://gta.fandom.com/wiki/Rockstar_Advanced_Game_Engine" rel="noopener noreferrer"&gt;Rockstar Advanced Game Engine (RAGE)&lt;/a&gt;&lt;br&gt;
Developer - &lt;a href="https://www.rockstarnorth.com/" rel="noopener noreferrer"&gt;Rockstar North&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Grand Theft Auto V aka GTAV is an action-adventure game with the first release in 2013. even though this game is nearly 8 years old, it is still a very popular game, with the online GTAV. GTAV is the latest game in the Grand Theft Auto franchise. there are rumors about the upcoming GTA 6 with a female protagonist but still, nothing official has been declared. GTAV is developed using Rockstar Advanced Game Engine (RAGE). RAGE game engine is a proprietary game engine developed by Rockstar Game Studios. GTAV game is second in the &lt;a href="https://en.wikipedia.org/wiki/List_of_best-selling_video_games" rel="noopener noreferrer"&gt;most sold video game of all time&lt;/a&gt; with over 140 million copies sold. although it is not listed, we can get an idea that rage is written using c++, from their &lt;a href="https://www.rockstargames.com/careers/openings/position/4368587003" rel="noopener noreferrer"&gt;careers page&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. &lt;a href="https://www.rockstargames.com/reddeadredemption2/" rel="noopener noreferrer"&gt;Red Dead Redemption 2&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Femlgsbgsk606ciuzpbww.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Femlgsbgsk606ciuzpbww.jpg" alt="Red Dead Redemption 2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Game engine - &lt;a href="https://gta.fandom.com/wiki/Rockstar_Advanced_Game_Engine" rel="noopener noreferrer"&gt;Rockstar Advanced Game Engine (RAGE)&lt;/a&gt;&lt;br&gt;
Developer - &lt;a href="https://www.rockstargames.com/" rel="noopener noreferrer"&gt;Rockstar Studios&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Red Dead Redemption 2 is a Western-themed action-adventure game released in 2018 by Rockstar Studios. this game is developed using Rockstar Advanced Game Engine (RAGE). this is the latest game of the Red Dead Redemption series. the game was first released for PlayStation 4 and Xbox One in October 2018, and for Microsoft Windows and Stadia in November 2019.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. &lt;a href="https://thewitcher.com/en/" rel="noopener noreferrer"&gt;The Witcher 3: Wild Hunt&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu8egzcd5a6hgy5q6yo66.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu8egzcd5a6hgy5q6yo66.jpg" alt="The Witcher 3: Wild Hunt"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Game engine - REDengine 3&lt;br&gt;
Developer -&lt;a href="https://www.cdprojekt.com/en/" rel="noopener noreferrer"&gt;CD Projekt Red&lt;/a&gt;&lt;br&gt;
Written in - C++, C#&lt;/p&gt;

&lt;p&gt;The Witcher 3: Wild Hunt developed by &lt;a href="https://www.cdprojekt.com/en/" rel="noopener noreferrer"&gt;CD Projekt Red&lt;/a&gt; aka CDPR a polish game studio. The Witcher 3 was released in 2015 as the latest game in the witcher franchise. The Witcher 3 is an action roleplay game based on book series written by &lt;a href="https://en.wikipedia.org/wiki/Andrzej_Sapkowski" rel="noopener noreferrer"&gt;Andrzej Sapkowski.&lt;/a&gt;. it is developed using REDengine 3 which is a proprietary game engine developed by CDPR. REDengine 3 is written using c++.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. &lt;a href="https://www.cyberpunk.net/" rel="noopener noreferrer"&gt;Cyberpunk 2077&lt;/a&gt;
&lt;/h2&gt;

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

&lt;p&gt;Game engine - REDengine 4&lt;br&gt;
Developer -&lt;a href="https://www.cdprojekt.com/en/" rel="noopener noreferrer"&gt;CD Projekt Red&lt;/a&gt;&lt;br&gt;
Written in - C++, C#&lt;/p&gt;

&lt;p&gt;Cyberpunk 2077 is the most anticipated game in 2020 which is also developed by CD Projekt Red. Cyberpunk 2077 is an action role-playing game. Cyberpunk 2077 plot is set in a dystopian future in 2077. Cyberpunk 2077 also featured starring Keanu Reaves in a role as Johnny Silverhand in-game. Cyberpunk 2077 uses their proprietary REDengine 4 which is a successor of REDengine 3 which is also written in c++.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. &lt;a href="https://www.ubisoft.com/en-gb/game/assassins-creed/valhalla" rel="noopener noreferrer"&gt;Assassin's Creed Valhalla&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn3da3azr5ofuthn3xnyp.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn3da3azr5ofuthn3xnyp.jpg" alt="Assassin's Creed Valhalla"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Game engine - AnvilNext 2.0&lt;br&gt;
Developer - &lt;a href="https://montreal.ubisoft.com/en/" rel="noopener noreferrer"&gt;Ubisoft Montreal&lt;/a&gt;&lt;br&gt;
Written in - C++, C#&lt;/p&gt;

&lt;p&gt;Assassin's Creed Valhalla is the latest release of the popular Assassin's Creed series released in 2020. this game is an action role-playing video game. this game is developed by Canadian-based Ubisoft Montreal Studios and published by Ubisoft. this is the latest release of the popular Assassin's Creed series.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. &lt;a href="https://www.guerrilla-games.com/play/horizon" rel="noopener noreferrer"&gt;Horizon Zero Dawn&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvejtlzhp3bmzsp32teev.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvejtlzhp3bmzsp32teev.jpg" alt="Horizon Zero Dawn"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Game engine - Decima&lt;br&gt;
Developer - &lt;a href="https://www.guerrilla-games.com/" rel="noopener noreferrer"&gt;Guerrilla Games&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Horizon Zero Dawn is an action role-playing game released in 2017. this is developed by Guerrilla Games which is based in Amsterdam and published by Sony Interactive Entertainment. Horizon Zero Dawn The story is set in the 31st century with advanced robotic creatures. this game is developed using the Decima game engine which is proprietary to Guerrilla Games. although it is not listed we can assume that Decima also uses c++ based on their &lt;a href="https://www.guerrilla-games.com/join/senior-game-programmer-1" rel="noopener noreferrer"&gt;careers page&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  7. &lt;a href="https://www.ubisoft.com/en-gb/game/far-cry/far-cry-5" rel="noopener noreferrer"&gt;Far Cry 5&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq3ipm3vnyr47bemh4ipc.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq3ipm3vnyr47bemh4ipc.jpg" alt="Far Cry 5"&gt;&lt;/a&gt;&lt;br&gt;
Game engine - Dunia Engine&lt;br&gt;
Developer - Originally developed by &lt;a href="https://en.wikipedia.org/wiki/Crytek" rel="noopener noreferrer"&gt;Crytek&lt;/a&gt; and modified by &lt;a href="https://montreal.ubisoft.com/en/" rel="noopener noreferrer"&gt;Ubisoft Montreal&lt;/a&gt;&lt;br&gt;
Written in - C++&lt;/p&gt;

&lt;p&gt;Far Cry 5 is a first-person shooter game released in 2018. this game is developed by Ubisoft Montreal and Ubisoft Toronto and published by Ubisoft. this game is developed using Dunia Engine. Dunia Engine is a software fork of the CryEngine that was originally developed by &lt;a href="https://en.wikipedia.org/wiki/Crytek" rel="noopener noreferrer"&gt;Crytek&lt;/a&gt;, with modifications made by &lt;a href="https://montreal.ubisoft.com/en/" rel="noopener noreferrer"&gt;Ubisoft Montreal&lt;/a&gt;.this game engine is written in c++.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. &lt;a href="https://www.epicgames.com/fortnite/en-US/home" rel="noopener noreferrer"&gt;Fortnite&lt;/a&gt;
&lt;/h2&gt;

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

&lt;p&gt;Game engine - &lt;a href="http://www.unrealengine.com/" rel="noopener noreferrer"&gt;Unreal Engine 4&lt;/a&gt;&lt;br&gt;
Developer - &lt;a href="https://www.epicgames.com/" rel="noopener noreferrer"&gt;Epic Games&lt;/a&gt;&lt;br&gt;
Written in - C++, C#&lt;/p&gt;

&lt;p&gt;Fortnite is an online video game developed by Epic Games and released in 2017.first this game came as a battle royal game and now it is expanded to several game modes. this game is developed by Unreal Engine 4 that is also developed by Epic Games. this game got a huge anticipation past year with game streamer playing this game.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. &lt;a href="http://www.dota2.com/" rel="noopener noreferrer"&gt;Dota 2&lt;/a&gt;
&lt;/h2&gt;

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

&lt;p&gt;Game engine - &lt;a href="https://www.valvesoftware.com/en/" rel="noopener noreferrer"&gt;Valve Corporation&lt;/a&gt;&lt;br&gt;
Developer - &lt;a href="https://www.epicgames.com/" rel="noopener noreferrer"&gt;Epic Games&lt;/a&gt;&lt;br&gt;
Written in - C++&lt;/p&gt;

&lt;p&gt;Defense of the ancient 2 aka DOTA2 is a multiplayer online battle arena (MOBA) video game developed and published by Valve Corporation. DOTA2 is a sequel to DOTA 1 which was a community-created mod for Blizzard Entertainment's Warcraft III: Reign of Chaos and its expansion pack. this game is developed and published by Valve Software which is an American video game and digital distribution company. Valve also owns the popular game digital distribution service &lt;a href="https://store.steampowered.com/" rel="noopener noreferrer"&gt;Steam&lt;/a&gt;. DOTA2 is developed using the Source 2 game engine. Source 2 is a proprietary game engine developed by Valve as the successor to the Source engine. Source 2 is written in c++.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. &lt;a href="https://innersloth.com/gameAmongUs.php" rel="noopener noreferrer"&gt;Among Us&lt;/a&gt;
&lt;/h2&gt;

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

&lt;p&gt;Game engine - &lt;a href="https://unity.com/" rel="noopener noreferrer"&gt;Unity&lt;/a&gt;&lt;br&gt;
Developer - &lt;a href="https://innersloth.com/" rel="noopener noreferrer"&gt;Innersloth&lt;/a&gt;&lt;br&gt;
Written in - C++ , C#&lt;/p&gt;

&lt;p&gt;Among Us is an online multiplayer social deduction game developed and published by American game studio Innersloth. this game got huge popularity in 2020 due to many well-known Twitch streamers and YouTubers playing it. this game is developed using the Unity game engine. Unity is a proprietary cross-platform game engine developed by &lt;a href="https://unity.com/" rel="noopener noreferrer"&gt;Unity Technologies&lt;/a&gt;. among us is available on platforms such as Andriod and IOS because this game uses Unity game engine.&lt;/p&gt;

&lt;h2&gt;
  
  
  11. &lt;a href="https://www.ea.com/games/apex-legends" rel="noopener noreferrer"&gt;Apex Legends&lt;/a&gt;
&lt;/h2&gt;

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

&lt;p&gt;Game engine - &lt;a href="https://www.valvesoftware.com/en/" rel="noopener noreferrer"&gt;Valve Corporation&lt;/a&gt;&lt;br&gt;
Developer - &lt;a href="https://www.epicgames.com/" rel="noopener noreferrer"&gt;Epic Games&lt;/a&gt;&lt;br&gt;
Written in - C++&lt;/p&gt;

&lt;p&gt;Apex Legends is a free-to-play first-person hero shooter battle royale game developed by Respawn Entertainment published by Electronic Arts. Apex Legends surpassed over 25 million players by the end of its first week, and 50 million within its first month with huge anticipation. The game is set in the same science fiction universe as Respawn Entertainment's Titanfall and Titanfall 2. The game is built using a modified version of Valve Corporation's Source engine.&lt;/p&gt;

&lt;h2&gt;
  
  
  12.&lt;a href="https://www.minecraft.net/en-us/" rel="noopener noreferrer"&gt;Minecraft&lt;/a&gt;
&lt;/h2&gt;

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

&lt;p&gt;Developer - &lt;a href="https://en.wikipedia.org/wiki/Mojang" rel="noopener noreferrer"&gt;Mojang Studios&lt;/a&gt;&lt;br&gt;
Written in - java&lt;/p&gt;

&lt;p&gt;Minecraft is a sandbox video game developed by Swedish video game studio &lt;a href="https://en.wikipedia.org/wiki/Mojang" rel="noopener noreferrer"&gt;Mojang&lt;/a&gt; released in 2009. The game was created by Markus "Notch" Persson in the Java programming language and later he set up a video game company, Mojang with the money earned from the game. Minecraft is developed using &lt;a href="https://www.lwjgl.org/" rel="noopener noreferrer"&gt;lwjgl&lt;/a&gt; library written in java. this is not considered as a game engine and used as a library to work with java API's. this game currently holds the title of &lt;a href="https://en.wikipedia.org/wiki/List_of_best-selling_video_games" rel="noopener noreferrer"&gt;best-selling video game of all time&lt;/a&gt; with over 200 million copies sold. this is also the only game in this list that is written other than in c family languages. &lt;/p&gt;

&lt;h3&gt;
  
  
  Thank you for reading until the end.
&lt;/h3&gt;

&lt;h3&gt;
  
  
  Please do correct me if I missed something, and also share your valuable thoughts in the comment section
&lt;/h3&gt;

&lt;h3&gt;
  
  
  What is your favorite game. Drop down in the comment section below.
&lt;/h3&gt;

&lt;h3&gt;
  
  
  Stay safe
&lt;/h3&gt;

&lt;h3&gt;
  
  
  Cheers 🥂 , Have a Nice Day.
&lt;/h3&gt;

</description>
      <category>gamedev</category>
    </item>
    <item>
      <title>Publish Gatsby Blog to GitHub Pages with GitHub Actions</title>
      <dc:creator>Rajitha Gunathilake</dc:creator>
      <pubDate>Sat, 15 Aug 2020 17:37:52 +0000</pubDate>
      <link>https://forem.com/rizkyrajitha/publish-gatsby-blog-to-github-pages-with-github-actions-2lh8</link>
      <guid>https://forem.com/rizkyrajitha/publish-gatsby-blog-to-github-pages-with-github-actions-2lh8</guid>
      <description>&lt;p&gt;This workflow will build and publish a &lt;a href="https://www.gatsbyjs.org"&gt;Gatsby&lt;/a&gt; blog to the &lt;a href="https://pages.github.com"&gt;GitHub Pages&lt;/a&gt; and notify status via &lt;a href="https://telegram.org"&gt;Telegram&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  My Workflow
&lt;/h3&gt;

&lt;p&gt;This workflow will build the code on &lt;code&gt;dev&lt;/code&gt; branch and then copy build files to the &lt;code&gt;master&lt;/code&gt; branch. Then it will commit those files and push them to publish the Blog on GitHub Pages.&lt;/p&gt;

&lt;p&gt;This workflow will trigger on 2 events&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;on push to dev branch&lt;/li&gt;
&lt;li&gt;cron job runs daily at 0800h
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;dev&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;schedule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;cron&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;8&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*"&lt;/span&gt;

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

&lt;/div&gt;


&lt;p&gt;cron job is used because my blog use &lt;a href="https://apod.nasa.gov/apod/astropix.html"&gt;nasa image of the day&lt;/a&gt; as it's background image and it is updating daily .This is not mandatory and can be omitted based on requirement .&lt;/p&gt;

&lt;p&gt;After that, workflow will delete everything in the &lt;code&gt;master&lt;/code&gt; branch and then checkout to &lt;code&gt;dev&lt;/code&gt; branch and start building . After building it will copy the build files(on public folder) to &lt;code&gt;master&lt;/code&gt; branch and push those changes to publish to GitHub Pages.&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="s"&gt;checkout master  branch&lt;/span&gt; &lt;span class="c1"&gt;# checkout master  branch&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;persist-credentials&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;false&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;remove all files&lt;/span&gt; &lt;span class="c1"&gt;# remove files.&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;rm -rf *&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;create CNAME file&lt;/span&gt; &lt;span class="c1"&gt;# remove files.&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;echo "rizkyrajitha.me" &amp;gt; CNAME&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;checkout dev  branch&lt;/span&gt; &lt;span class="c1"&gt;#checkout dev  branch into temp folder.&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dev&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;temp&lt;/span&gt;
          &lt;span class="na"&gt;persist-credentials&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;false&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;run yarn install and build&lt;/span&gt; &lt;span class="c1"&gt;# go to temp folder and run npm build to create files.&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;cd temp&lt;/span&gt;
          &lt;span class="s"&gt;yarn install&lt;/span&gt;
          &lt;span class="s"&gt;yarn build&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;move files&lt;/span&gt; &lt;span class="c1"&gt;# move the public built files into root dir and remove others.&lt;/span&gt;
        &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ success() }}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;mv temp/public/* ./&lt;/span&gt;
          &lt;span class="s"&gt;rm -rf temp&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;Commit files for change&lt;/span&gt; &lt;span class="c1"&gt;# commit&lt;/span&gt;
        &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ success() }}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;git config --local user.email "action@github.com"&lt;/span&gt;
          &lt;span class="s"&gt;git config --local user.name "GitHub Action"&lt;/span&gt;
          &lt;span class="s"&gt;git add .&lt;/span&gt;
          &lt;span class="s"&gt;git commit -m "Add changes"&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;Push changes&lt;/span&gt; &lt;span class="c1"&gt;#push files into master branch&lt;/span&gt;
        &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ success() }}&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ad-m/github-push-action@master&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;BRANCH&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;master&lt;/span&gt;
          &lt;span class="na"&gt;github_token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
          &lt;span class="na"&gt;force&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

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

&lt;/div&gt;


&lt;p&gt;After building and publishing blog a telegram message is send to a specified chat to notify the workflow status(success , Cancelled or failure) . &lt;code&gt;TELEGRAM_BOT_ID&lt;/code&gt; , &lt;code&gt;TELEGRAM_CHAT_ID&lt;/code&gt; is required .  other fields can be omitted on your preference&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="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;Notify via TELEGRAM BOT&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;RizkyRajitha/github_actions_notify_telegram@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;TELEGRAM_BOT_ID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.TELEGRAM_BOT_ID }}&lt;/span&gt;
          &lt;span class="na"&gt;TELEGRAM_CHAT_ID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.TELEGRAM_CHAT_ID }}&lt;/span&gt;
          &lt;span class="na"&gt;CUSTOMMESSAGESUCCESS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Published&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="se"&gt;\xF0\x9F\x9A\x80&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Blog&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;via&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;GitHub&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Actions"&lt;/span&gt;
          &lt;span class="na"&gt;CUSTOMMESSAGESFAILURE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Publishing&lt;/span&gt;&lt;span class="nv"&gt;  &lt;/span&gt;&lt;span class="s"&gt;Blog&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="se"&gt;\xE2\x9B\x94&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;via&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;GitHub&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Actions&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Failed"&lt;/span&gt;
          &lt;span class="na"&gt;CUSTOMMESSAGESCANCELLED&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Publishing&lt;/span&gt;&lt;span class="nv"&gt;  &lt;/span&gt;&lt;span class="s"&gt;Blog&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="se"&gt;\xE2\x9B\x94&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;via&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;GitHub&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Actions&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Cancelled"&lt;/span&gt;
          &lt;span class="na"&gt;JOBSTATUS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ job.status }}&lt;/span&gt;
          &lt;span class="na"&gt;GITHUB_RUN_NUMBER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.run_number }}&lt;/span&gt;
          &lt;span class="na"&gt;GITHUB_REPOSITORY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.repository }}&lt;/span&gt;
          &lt;span class="na"&gt;GITHUB_RUN_ID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.run_id }}&lt;/span&gt;
          &lt;span class="na"&gt;GITHUB_ACTOR&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.actor }}&lt;/span&gt;
          &lt;span class="na"&gt;GITHUB_EVENT_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.event_name }}&lt;/span&gt;
          &lt;span class="na"&gt;GITHUB_SHA&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.sha }}&lt;/span&gt;


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

&lt;/div&gt;


&lt;p&gt;Actions used&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/actions/checkout"&gt;actions/checkout@v2&lt;/a&gt; : for checking out branches &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/ad-m/github-push-action"&gt;ad-m/github-push-action@master&lt;/a&gt;  : push build files to master branch , published via GitHub Pages&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/%5BRizkyRajitha/github_actions_notify_telegram"&gt;RizkyRajitha/github_actions_notify_telegram@v1&lt;/a&gt; : send telegram notifications&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Submission Category:
&lt;/h3&gt;

&lt;p&gt;DIY Deployments&lt;/p&gt;
&lt;h3&gt;
  
  
  Yaml File or Link to Code
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/RizkyRajitha/RizkyRajitha.github.io/blob/dev/.github/workflows/main.yml"&gt;main.yml file&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Telegram Notify action Repo&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--i3JOwpme--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-ba8488d21cd8ee1fee097b8410db9deaa41d0ca30b004c0c63de0a479114156f.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/RizkyRajitha"&gt;
        RizkyRajitha
      &lt;/a&gt; / &lt;a href="https://github.com/RizkyRajitha/github_actions_notify_telegram"&gt;
        github_actions_notify_telegram
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Telegram Notification for Github action
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;h1&gt;
Telegram Notification for Github action&lt;/h1&gt;
&lt;p&gt;
  &lt;a href="https://telegram.org/" rel="nofollow"&gt;&lt;img alt="Telegram icon" src="https://camo.githubusercontent.com/91bdc89e65022b9befe1428acbdac06f03eccfb22c47b5ca34527253ceee1a6b/68747470733a2f2f74656c656772616d2e6f72672f696d672f745f6c6f676f2e7376673f31"&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;h1&gt;
&lt;a href="https://dev.to/rizkyrajitha/get-notifications-with-telegram-bot-537l" rel="nofollow"&gt; Guide to create a bot and adding to telegram chat &lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://raw.githubusercontent.com/RizkyRajitha/github_actions_notify_telegram/main/img-telegram-chat.jpg"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ay-YzZth--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/RizkyRajitha/github_actions_notify_telegram/main/img-telegram-chat.jpg" alt="telegram chat message example "&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
add TELEGRAM_BOT_ID and TELEGRAM_CHAT_ID to the secrets in github repo&lt;/h3&gt;
&lt;p&gt;Example&lt;/p&gt;
&lt;div class="snippet-clipboard-content position-relative"&gt;&lt;pre&gt;&lt;code&gt;
name: Telegram Notification


on:
  push:
    branches: [master]

jobs:
  Publish:
    runs-on: [ubuntu-latest]

    steps:
      - name: checkout master  branch # checkout master  branch
        uses: actions/checkout@v2
        with:
          persist-credentials: false

      - name: run yarn install and build # go to temp folder and run npm build to create files.
        run: |
          cd temp
          yarn install
          yarn build

      - name: Notify via TELEGRAM BOT
        uses: RizkyRajitha/github_actions_notify_telegram@v1
        with:
          TELEGRAM_BOT_ID: ${{ secrets.TELEGRAM_BOT_ID }}
          TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
          CUSTOMMESSAGESUCCESS: "Published \xF0\x9F\x9A\x80 Blog via GitHub Actions"
          CUSTOMMESSAGESFAILURE: "Publishing  Blog \xE2\x9B\x94 via GitHub Actions Failed"
          CUSTOMMESSAGESCANCELLED: "Publishing  Blog \xE2\x9B\x94 via GitHub Actions Cancelled"
          JOBSTATUS: ${{ job.status }}
          GITHUB_RUN_NUMBER: ${{ github.run_number }}
          GITHUB_REPOSITORY: ${{ github.repository }}
          GITHUB_RUN_ID: ${{ github.run_id }}
          GITHUB_ACTOR: ${{ github.actor }}
          GITHUB_EVENT_NAME: ${{ github.event_name }}
          GITHUB_SHA: ${{ github.sha }}


&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1&gt;
Create a JavaScript&lt;/h1&gt;…&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/RizkyRajitha/github_actions_notify_telegram"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;h3&gt;
  
  
  Additional Resources / Info
&lt;/h3&gt;

&lt;p&gt;Blog using this workflow&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--i3JOwpme--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-ba8488d21cd8ee1fee097b8410db9deaa41d0ca30b004c0c63de0a479114156f.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/RizkyRajitha"&gt;
        RizkyRajitha
      &lt;/a&gt; / &lt;a href="https://github.com/RizkyRajitha/RizkyRajitha.github.io"&gt;
        RizkyRajitha.github.io
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Reference &lt;a href="https://github.community/t/user-github-pages-site-auto-deploy-from-another-branch-on-push/17912"&gt;https://github.community/t/user-github-pages-site-auto-deploy-from-another-branch-on-push/17912&lt;/a&gt;&lt;/p&gt;

</description>
      <category>actionshackathon</category>
      <category>opensource</category>
      <category>github</category>
      <category>telegram</category>
    </item>
    <item>
      <title>Using Airtable as a database</title>
      <dc:creator>Rajitha Gunathilake</dc:creator>
      <pubDate>Sun, 17 May 2020 12:44:44 +0000</pubDate>
      <link>https://forem.com/rizkyrajitha/using-airtable-as-a-database-421d</link>
      <guid>https://forem.com/rizkyrajitha/using-airtable-as-a-database-421d</guid>
      <description>&lt;p&gt;Hi everyone,&lt;/p&gt;

&lt;p&gt;I recently used &lt;a href="https://airtable.com/"&gt;Airtable&lt;/a&gt; and found out that it has an awesome API, so we can use it as a simple yet useful way as a database.&lt;/p&gt;

&lt;p&gt;so let's get started&lt;/p&gt;

&lt;p&gt;first, we will create an npm project and spin up an expressjs server &lt;/p&gt;

&lt;p&gt;if you are new to express check out my post on hello world in express. &lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/rizkyrajitha" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MkH1vT_b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--jnnWRKA1--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/247664/bb29688b-6d69-444c-b5fb-d5b4365bb671.jpeg" alt="rizkyrajitha"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/rizkyrajitha/build-your-first-expressjs-server-from-scratch-52c3" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Build your first ExpressJs server from scratch.&lt;/h2&gt;
      &lt;h3&gt;Rajitha Gunathilake ・ Dec 1 '19 ・ 4 min read&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#node&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#express&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;so after initializing the project lets install dependencies to start the server. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;body-parser -  a middleware to parse the body of incoming requests&lt;/li&gt;
&lt;li&gt;cors  - ho handle &lt;a href="https://en.wikipedia.org/wiki/Cross-origin_resource_sharing"&gt;cors&lt;/a&gt; headers &lt;/li&gt;
&lt;li&gt;express -  to spin up our expressjs server&lt;/li&gt;
&lt;li&gt;morgan - a middleware utility tool that logs the server events (this is not essential but useful for debugging)&lt;/li&gt;
&lt;li&gt;node-fetch - fetch API for node environment &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;npm i express cors morgan body-parser node-fetch&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0zVGbt1D--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/xqk2hrsecb88607ixcdt.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0zVGbt1D--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/xqk2hrsecb88607ixcdt.PNG" alt="npm deps" width="719" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;so after installing everything lets create our &lt;code&gt;app.js&lt;/code&gt; file.&lt;br&gt;
here we will create our express server&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const express = require("express");
const app = express();
const cors = require("cors");
const bp = require("body-parser");
const fetch = require("node-fetch");

app.use(cors());
app.use(bp.urlencoded({ extended: false }));
app.use(bp.json());
app.use(require("morgan")("dev"));

const port = process.env.PORT || 5000;


app.listen(port, () =&amp;gt; {
  console.log("listning on " + port);
});


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

&lt;/div&gt;


&lt;p&gt;and run this by &lt;code&gt;node app.js&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eq_-rp2g--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/3bs1ssm6yeb6lb0a4o2v.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eq_-rp2g--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/3bs1ssm6yeb6lb0a4o2v.PNG" alt="server running" width="356" height="67"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;now our server-side is up let's peek to Airtable. &lt;/p&gt;

&lt;p&gt;create a &lt;a href="https://airtable.com/"&gt;Airtable&lt;/a&gt; account, and create a new base.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VEFtoZMn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/wrvi8sgprk654r5f66fe.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VEFtoZMn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/wrvi8sgprk654r5f66fe.PNG" alt="create a base" width="880" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;next name it &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PEEleG4_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/7u965hzl7i076qhxv6u1.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PEEleG4_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/7u965hzl7i076qhxv6u1.PNG" alt="name base" width="581" height="552"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;and then open a new base. and you will see something similar to this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ef1I-ulO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/92v4vmjlhrrjl0e2j4p2.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ef1I-ulO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/92v4vmjlhrrjl0e2j4p2.PNG" alt="Air table table" width="880" height="206"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;now customize it as you like. I will add two fields &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;country&lt;/code&gt;.&lt;br&gt;
and I will add few records so when we fetch data it has somethings to show.&lt;/p&gt;

&lt;p&gt;after everything, mine looks like this. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Po1DV2X9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/gaa6bnkbsbgjex7w0vg9.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Po1DV2X9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/gaa6bnkbsbgjex7w0vg9.PNG" alt="Airtable populated" width="539" height="364"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;now let's head to account to get our &lt;code&gt;API KEY&lt;/code&gt; which we will use to authenticate with the Airtable API.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UyBNvu1r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/dgw5cil5i4ux03cylkwa.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UyBNvu1r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/dgw5cil5i4ux03cylkwa.jpg" alt="api key" width="880" height="680"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;we also need our base id to identify our base and table name . to get those data, visit the API docs page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2Ph0CPvC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/cl0tu2oc99i1or7olagl.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2Ph0CPvC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/cl0tu2oc99i1or7olagl.jpg" alt="Api docs" width="880" height="680"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;select base you created &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LhUS2eKq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/gpz0t3m49cs2izbbbygt.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LhUS2eKq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/gpz0t3m49cs2izbbbygt.jpg" alt="select base" width="768" height="732"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;now copy base id and table name from the docs.  base id is led by &lt;code&gt;app&lt;/code&gt; and table name is the name you gave when you customize the table.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uQt674p1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/yriyf9ivndjaa6xwd0tb.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uQt674p1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/yriyf9ivndjaa6xwd0tb.jpg" alt="docs " width="880" height="213"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;after creating an API key and getting necessary information,&lt;br&gt;
 let's head back to our server.&lt;/p&gt;

&lt;p&gt;I created a separate folder for configs and keys, but you can also directly use the API key in the code since this is development purposes only. but make sure you don't commit your keys with the code. &lt;/p&gt;

&lt;p&gt;Airtable gives us 4 basic operations with the API&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read&lt;/li&gt;
&lt;li&gt;Create&lt;/li&gt;
&lt;li&gt;Update&lt;/li&gt;
&lt;li&gt;Delete&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Reading Table
&lt;/h3&gt;

&lt;p&gt;I will create a get route &lt;code&gt;http://localhost:5000/view&lt;/code&gt; to view existing data in our table&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const express = require("express");
const app = express();
const cors = require("cors");
const bp = require("body-parser");
const fetch = require("node-fetch");

app.use(cors());
app.use(bp.urlencoded({ extended: false }));
app.use(bp.json());
app.use(require("morgan")("dev"));

const AIRTABLEAPI = require("./config/env").airtableapikey; // import airtable api key 
const AIRTABLEBASEID = require("./config/env").airtablebaseid;// import airtable base  id 
const AIRTABLETABLENAME = "seriescharacters"; // table name

const port = process.env.PORT || 5000;

app.get("/view", (req, res) =&amp;gt; {

//we need to send a "GET" request with our base id table name and our API key to get the existing data on our table. 

  fetch(
    `https://api.airtable.com/v0/${AIRTABLEBASEID}/${AIRTABLETABLENAME}?view=Grid%20view`,
    {
      headers: { Authorization: `Bearer ${AIRTABLEAPI}` } // API key
    }
  )
    .then((res) =&amp;gt; res.json())
    .then((result) =&amp;gt; {
      console.log(result);
      res.json(result);
    })
    .catch((err) =&amp;gt; {
      console.log(err);
    });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;if we send a &lt;code&gt;GET&lt;/code&gt; request to &lt;code&gt;http://localhost:5000/view&lt;/code&gt; via &lt;a href="https://www.postman.com/"&gt;postman&lt;/a&gt; we will get a response with our existing data in &lt;code&gt;seriescharacters&lt;/code&gt; table&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Aa1DhcHl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/f667qg9bmr77yxw8cnjn.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Aa1DhcHl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/f667qg9bmr77yxw8cnjn.PNG" alt="postman output" width="729" height="739"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;console output &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tXc8EdXe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/kihkf34hwft7opnliagr.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tXc8EdXe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/kihkf34hwft7opnliagr.PNG" alt="console" width="448" height="227"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Create new record
&lt;/h3&gt;

&lt;p&gt;now let's add a new record by creating a &lt;code&gt;POST&lt;/code&gt; route &lt;code&gt;http://localhost:5000/create&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;create handler&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.post("/create", (req, res) =&amp;gt; {
  console.log(req.body);

  var datain = req.body;

  var payload = {
    records: [
      {
        fields: datain,
      },
    ],
  };

//we need to send a "POST" request with our base id, table name, our API key, and send a body with the new data we wish to add.

  fetch(`https://api.airtable.com/v0/${AIRTABLEBASEID}/${AIRTABLETABLENAME}`, {
    method: "post", // make sure it is a "POST request"
    body: JSON.stringify(payload),
    headers: {
      Authorization: `Bearer ${AIRTABLEAPI}`,   // API key
      "Content-Type": "application/json",  // we will recive a json object
    },
  })
    .then((res) =&amp;gt; res.json())
    .then((result) =&amp;gt; {
      console.log(result);
      res.json(result);
    })
    .catch((err) =&amp;gt; {
      console.log(err);
    });
});

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

&lt;/div&gt;


&lt;p&gt;if we send a &lt;code&gt;POST&lt;/code&gt; request to &lt;code&gt;http://localhost:5000/create&lt;/code&gt; with our data via &lt;a href="https://www.postman.com/"&gt;postman&lt;/a&gt; we will get a response with our data including the one we just added &lt;code&gt;seriescharacters&lt;/code&gt; table.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MPe3a9-p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/npuxxo2vrkbl3k52q2vy.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MPe3a9-p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/npuxxo2vrkbl3k52q2vy.jpg" alt="Post man create" width="688" height="630"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;also, we can see the updated table in real-time from Airtable.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UhJ8aMRn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/9fqbqp0x98xcsdft9ng8.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UhJ8aMRn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/9fqbqp0x98xcsdft9ng8.PNG" alt="Alt Text" width="463" height="372"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Updating a record
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;update handler&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.post("/update", (req, res) =&amp;gt; {
  console.log(req.body);

  var datain = req.body;

  var payload = {
    records: [
      {
        id: datain.id,
        fields: datain.updatedata,
      },
    ],
  };

//to update a record we have to send the new record with it's the id to Airtable API. 


  fetch(`https://api.airtable.com/v0/${AIRTABLEBASEID}/${AIRTABLETABLENAME}`, {
    method: "patch", // make sure it is a "PATCH request"
    body: JSON.stringify(payload),
    headers: {
      Authorization: `Bearer ${AIRTABLEAPI}`, // API key
      "Content-Type": "application/json",
    },
  })
    .then((res) =&amp;gt; res.json())
    .then((result) =&amp;gt; {
      console.log(result);
      res.json(result);
    })
    .catch((err) =&amp;gt; {
      console.log(err);
    });
});

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

&lt;/div&gt;


&lt;p&gt;if we send a &lt;code&gt;POST&lt;/code&gt; request to &lt;code&gt;http://localhost:5000/update&lt;/code&gt; with our data via &lt;a href="https://www.postman.com/"&gt;postman&lt;/a&gt; we will get a response with the updated record.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cxZrhc5y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/0pjavy4biqwe2vl9gh15.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cxZrhc5y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/0pjavy4biqwe2vl9gh15.jpg" alt="postman update" width="790" height="644"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Deleting a record
&lt;/h3&gt;

&lt;p&gt;now let's delete a record by creating a &lt;code&gt;POST&lt;/code&gt; route &lt;code&gt;http://localhost:5000/delete&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;delete handler&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.post("/delete", (req, res) =&amp;gt; {
  console.log(req.body);

//we need to send a "DELETE" request with our base id table name, the id of the record we wish to delete and our API key to get the existing data on our table. 

  fetch( `https://api.airtable.com/v0/${AIRTABLEBASEID}/${AIRTABLETABLENAME}/${req.body.id}`,
    {
      method: "delete", // make sure it is a "DELETE request"
      // body: JSON.stringify(payload),
      headers: {
        Authorization: `Bearer ${AIRTABLEAPI}`, // API key
        //   "Content-Type": "application/json",
      },
    }
  )
    .then((res) =&amp;gt; res.json())
    .then((result) =&amp;gt; {
      console.log(result);
      res.json(result);
    })
    .catch((err) =&amp;gt; {
      console.log(err);
    });
});

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

&lt;/div&gt;


&lt;p&gt;if we send a &lt;code&gt;POST&lt;/code&gt; request to &lt;code&gt;http://localhost:5000/delete&lt;/code&gt; with the id of the record we need to delete via &lt;a href="https://www.postman.com/"&gt;postman&lt;/a&gt; we will get a response with delete record id and deleted flag.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GnfrC3Xu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/2cwzqwdqgoib36nhpunj.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GnfrC3Xu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/2cwzqwdqgoib36nhpunj.jpg" alt="postman delete" width="650" height="490"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We successfully went through all the CRUD operations in Airtable 😎.&lt;/p&gt;

&lt;p&gt;This is very useful if you have a spreadsheet and you need to update data programmatically from your apps .plus Airtable has many more features other than spreadsheets, so you can suit your needs.&lt;/p&gt;

&lt;p&gt;final app.js file&lt;/p&gt;


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



&lt;h2&gt;
  
  
  Thank you for reading until the end
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Stay safe
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Cheers 🥂 , Have a Nice Day.
&lt;/h2&gt;

</description>
      <category>airtable</category>
      <category>express</category>
      <category>node</category>
      <category>database</category>
    </item>
    <item>
      <title>TryCargo One Click Store maker for your Business - twiliohackathon</title>
      <dc:creator>Rajitha Gunathilake</dc:creator>
      <pubDate>Tue, 07 Apr 2020 14:40:29 +0000</pubDate>
      <link>https://forem.com/rizkyrajitha/trycargo-one-click-store-maker-for-your-business-twiliohackathon-l2d</link>
      <guid>https://forem.com/rizkyrajitha/trycargo-one-click-store-maker-for-your-business-twiliohackathon-l2d</guid>
      <description>&lt;h2&gt;
  
  
  TryCargo 👀 what is that?
&lt;/h2&gt;

&lt;p&gt;TryCargo is a platform build for small scale stores to create their online stores without a fuss.&lt;/p&gt;

&lt;h2&gt;
  
  
  Motivation
&lt;/h2&gt;

&lt;p&gt;we got this idea from the lock down of our country because of recent pandemic of COVID19 . because of the lock down people cannot reach stores to get their supplies and most of stores even supermarket in our country lacks IT infrastructure . because of that many people couldn't get supplies . hence we came up with TryCargo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem addressed 🛒
&lt;/h2&gt;

&lt;p&gt;most of the small scale stores lack IT infrastructure so they cannot compete in the online market. &lt;br&gt;
and small scale business cannot afford those it infrastructure because they lack the capital investment and it involves huge risk.&lt;br&gt;
but with this TryCargo platform will give the opportunity to create their own online store with great quality without a hustle.&lt;/p&gt;
&lt;h2&gt;
  
  
  Design of TryCargo 📊
&lt;/h2&gt;

&lt;p&gt;tryCrago will cater 2 types of users &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;shop owner - shop owner can start the business with our platform&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;online customer - online customers can browse trycargo to find their needs and order them from their respective online stores&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  high-level use case diagram
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aw6Y8o-G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/dijjqfsto/image/upload/v1586266872/Untitled_Diagram_8_xbbhox.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aw6Y8o-G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/dijjqfsto/image/upload/v1586266872/Untitled_Diagram_8_xbbhox.png" alt="high level use case diagram"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  features of trycargo
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;crate online store page with the details about the business - shop owner 🧔&lt;/li&gt;
&lt;li&gt;online store page can be customized by adding and editing and removing products - shop owner 🧔 &lt;/li&gt;
&lt;li&gt;view pending order and order history - shop owner 🧔&lt;/li&gt;
&lt;li&gt;send notifications about order dispatch and order arrived - shop owner 🧔&lt;/li&gt;
&lt;li&gt;browse online stores - customers 🙍‍♂️ &lt;/li&gt;
&lt;li&gt;add and  remove items from shopping cart - customers 🙍‍♂️&lt;/li&gt;
&lt;li&gt;place orders - customers 🙍‍♂️&lt;/li&gt;
&lt;li&gt;view previous orders - customers 🙍‍♂️&lt;/li&gt;
&lt;li&gt;add comments about stores and products - customers 🙍‍♂️&lt;/li&gt;
&lt;li&gt;order tracking for customers 🙍‍♂️&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Payments 💰
&lt;/h2&gt;

&lt;p&gt;TryCargo platform will support cash on delivery payment system.&lt;/p&gt;
&lt;h2&gt;
  
  
  Further implementations ✨
&lt;/h2&gt;

&lt;p&gt;the online payment system will depend on the time, and if we have enough time we will try to implement it too. but we will keep in the safe zone for now. &lt;/p&gt;
&lt;h2&gt;
  
  
  Tech stack ⚙
&lt;/h2&gt;

&lt;p&gt;Frontend - react&lt;br&gt;
Backend - nodejs&lt;br&gt;
Database - MongoDB&lt;br&gt;
Sms notifications - twillio api&lt;br&gt;
Email notifications - sendgrid by twillio&lt;/p&gt;

&lt;p&gt;This is a project came up with me and my friend Ishanka, and we will be working on this together. &lt;/p&gt;


&lt;div class="ltag__user ltag__user__id__240576"&gt;
  
    .ltag__user__id__240576 .follow-action-button {
      background-color: #000000 !important;
      color: #ffffff !important;
      border-color: #000000 !important;
    }
  
    &lt;a href="/ishankadsenevirathne" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KjHoFMrk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--Fp65tLL1--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/240576/817cd6e5-fdc0-4932-820c-6ffed7cbb5ca.jpg" alt="ishankadsenevirathne image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/ishankadsenevirathne"&gt;Ishanka D Senevirathne&lt;/a&gt;
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/ishankadsenevirathne"&gt;My thirst for knowledge cannot be quenched.&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;working github repo &lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--i3JOwpme--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-ba8488d21cd8ee1fee097b8410db9deaa41d0ca30b004c0c63de0a479114156f.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/RizkyRajitha"&gt;
        RizkyRajitha
      &lt;/a&gt; / &lt;a href="https://github.com/RizkyRajitha/trycargo"&gt;
        trycargo
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;h2&gt;
TryCargo 👀 what is that?&lt;/h2&gt;
&lt;p&gt;TryCargo is a platform build for small scale stores to create online ordering systems.&lt;/p&gt;
&lt;h2&gt;
Problem addressed 🛒
&lt;/h2&gt;
&lt;p&gt;most of the small scale stores lack IT infrastructure so they cannot compete in the online market
and small scale business cannot afford those it infrastructure because they lack the capital investment and it involves huge risk
but with this TryCargo platform will give the opportunity to create their own online store with great quality without a hustle.&lt;/p&gt;
&lt;h2&gt;
design of TryCargo 📊
&lt;/h2&gt;
&lt;p&gt;tryCrago will cater 2 types of users&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;shop owner - shop owner can start the business with our platform&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;online customer - online customers can browse trycargo to find their needs and order them from their respective online stores&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
high-level use case diagram&lt;/h3&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://camo.githubusercontent.com/b91684a8a407012d0fd3f5717b8dfcb86985ed69fa5429811397b619310d50d1/68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f64696a6a716673746f2f696d6167652f75706c6f61642f76313538363236363837322f556e7469746c65645f4469616772616d5f385f786262686f782e706e67"&gt;&lt;img src="https://camo.githubusercontent.com/b91684a8a407012d0fd3f5717b8dfcb86985ed69fa5429811397b619310d50d1/68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f64696a6a716673746f2f696d6167652f75706c6f61642f76313538363236363837322f556e7469746c65645f4469616772616d5f385f786262686f782e706e67" alt="high level use case diagram"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
features of trycargo&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;crate online store page with the details about the business - shop owner 🧔
&lt;/li&gt;
&lt;li&gt;online store page can be customized…&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/RizkyRajitha/trycargo"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Upadates
&lt;/h2&gt;

&lt;h2&gt;
  
  
  2020 / 4 / 8
&lt;/h2&gt;

&lt;p&gt;created login signup for shop owner and customer.&lt;/p&gt;

&lt;h2&gt;
  
  
  2020 / 4 /11
&lt;/h2&gt;

&lt;p&gt;created landing page and login for users&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Bgs9WAXa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://steamuserimages-a.akamaihd.net/ugc/1020570582601459929/FDE25EF202FCCBC49AAB148BE79F2B920C7FDF21/" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Bgs9WAXa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://steamuserimages-a.akamaihd.net/ugc/1020570582601459929/FDE25EF202FCCBC49AAB148BE79F2B920C7FDF21/" alt="landing page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---s9rdf5j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://steamuserimages-a.akamaihd.net/ugc/1020570582601461618/37AF362918B3443D5F11FF06BDC3D36E1F97F2B0/" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---s9rdf5j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://steamuserimages-a.akamaihd.net/ugc/1020570582601461618/37AF362918B3443D5F11FF06BDC3D36E1F97F2B0/" alt="login"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2020 / 4 /16
&lt;/h2&gt;

&lt;p&gt;created dashboard for customer and shop owner , add items to shop .&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ff7hjJW9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.discordapp.com/attachments/464811671773249539/700414549899083807/Annotation_2020-04-17_000515.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ff7hjJW9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.discordapp.com/attachments/464811671773249539/700414549899083807/Annotation_2020-04-17_000515.png" alt="customer dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hkufDsVw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.discordapp.com/attachments/464811671773249539/700416069784830433/Annotation_2020-04-17_001304.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hkufDsVw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.discordapp.com/attachments/464811671773249539/700416069784830433/Annotation_2020-04-17_001304.png" alt="shop owner dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>twiliohackathon</category>
    </item>
    <item>
      <title>How i create a COVID-19 Data Visualization with Nivo and React</title>
      <dc:creator>Rajitha Gunathilake</dc:creator>
      <pubDate>Sat, 04 Apr 2020 06:27:51 +0000</pubDate>
      <link>https://forem.com/rizkyrajitha/how-i-create-a-covid-19-data-visualization-with-nivo-and-react-516h</link>
      <guid>https://forem.com/rizkyrajitha/how-i-create-a-covid-19-data-visualization-with-nivo-and-react-516h</guid>
      <description>&lt;p&gt;Hi everyone,&lt;/p&gt;

&lt;p&gt;I recently made a Data Visualization about  Sri Lanka COVID-19 statistics.&lt;br&gt;
by the time I am writing, there was no public API  to get past statistics of the COVID-19 pandemic but there was an API to get the most recent statistics. So i created one 😉.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Jb3v56Hp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/drzzvks8hg34vu6g3ss9.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Jb3v56Hp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/drzzvks8hg34vu6g3ss9.PNG" alt="website screenshot"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Automate updates to database
&lt;/h2&gt;

&lt;p&gt;I created a database filled out past data that was missing and created a script that updates my database with the most recent data, by that data is automatically updated without any manual tasks. for database i used MongoDB since i am very comfortable with MongoDB and also MongoDB atlas give a great free plan.&lt;/p&gt;

&lt;p&gt;i wrote a simple JavaScript script to make a request for every 30 seconds and then if there are any latest updates update my database with those changes.pretty simple right.even though it can run locally to push changes to database , i used a aws ec2 instance to run it because it is more convenient that running it in my local PC forever.i know ec2 instance is a overkill for this purpose but this was the only option i had at the time. and if you know a better option please let me know in the comments section  ,  like pretty please 🙄 .&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ksYa9Baz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/n4d1xbl2hfojislckn9k.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ksYa9Baz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/n4d1xbl2hfojislckn9k.gif" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Creating frontend
&lt;/h2&gt;

&lt;p&gt;Frontend is build with &lt;a href="https://reactjs.org/"&gt;React&lt;/a&gt; and styled with &lt;a href="https://getbootstrap.com/"&gt;bootstrap&lt;/a&gt; , nothing fancy their. I tried out a few different data visualization libraries, but for me &lt;a href="https://nivo.rocks/line/"&gt;nivo&lt;/a&gt; worked out the best. it was very clear to understand and packed with tons of features.&lt;br&gt;
&lt;a href="https://nivo.rocks/line/"&gt;nivo&lt;/a&gt; also supports responsive layouts, it looks fluid in mobile displays.it is very important to consider building responsive web apps since nowadays most of the users visit with their mobile devices. so a reasonable amount of your website's traffic will come from mobiles.&lt;/p&gt;
&lt;h2&gt;
  
  
  Creating API
&lt;/h2&gt;

&lt;p&gt;for my API, I chose to go with &lt;a href="https://aws.amazon.com/lambda/"&gt;AWS lambda&lt;/a&gt; because it is easy to spin up, and this implementation only needed one API endpoint. so I configured lambda function to fetch data and send them to frontend. &lt;/p&gt;
&lt;h2&gt;
  
  
  Deployment
&lt;/h2&gt;

&lt;p&gt;After wiring up everything, I used &lt;a href="https://zeit.co/home"&gt;zeit&lt;/a&gt; to deploy the frontend, because it is really easy to deploy and it supports git integrations.used MongoDB atlas for the production database, and aws lambda for API and aws ec2 to run automate script.&lt;/p&gt;
&lt;h2&gt;
  
  
  Telegram bot
&lt;/h2&gt;

&lt;p&gt;As a part of this project i made a telegram channel with a telegram bot , to sent notifications about the pandemic.this was embedded to the automated script to sent a post request to telegram API in new update. more details on how to send telegram messages with bots is in this tutorial.&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/rizkyrajitha" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MkH1vT_b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--jnnWRKA1--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/247664/bb29688b-6d69-444c-b5fb-d5b4365bb671.jpeg" alt="rizkyrajitha"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/rizkyrajitha/get-notifications-with-telegram-bot-537l" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Sending messages with Telegram bot&lt;/h2&gt;
      &lt;h3&gt;Rajitha Gunathilake ・ Nov 10 '19 ・ 3 min read&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#bots&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#telegram&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#http&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


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


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--i3JOwpme--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-ba8488d21cd8ee1fee097b8410db9deaa41d0ca30b004c0c63de0a479114156f.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/RizkyRajitha"&gt;
        RizkyRajitha
      &lt;/a&gt; / &lt;a href="https://github.com/RizkyRajitha/covidgraph"&gt;
        covidgraph
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Graphical Representation of COVID-19 report Sri Lanka
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;h2&gt;
Graphical Representation of covid-19 report Sri Lanka&lt;/h2&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://camo.githubusercontent.com/50834c6ed85495f0432473d414892f18d9b73dd9119ed7b59c1cd24649998c91/68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f64696a6a716673746f2f696d6167652f75706c6f61642f76313538353439323638362f636f7669645f636d787137732e706e67"&gt;&lt;img src="https://camo.githubusercontent.com/50834c6ed85495f0432473d414892f18d9b73dd9119ed7b59c1cd24649998c91/68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f64696a6a716673746f2f696d6167652f75706c6f61642f76313538353439323638362f636f7669645f636d787137732e706e67" alt="website image"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://camo.githubusercontent.com/ff52b39ecceafe71aa63efe4688cd34f03eeb88b96b860cb34d86508717f54bf/68747470733a2f2f62616467656e2e6e65742f62616467652f2545322539362542322532304465706c6f79253230746f2532304e6f772f242532306e6f7725323052697a6b7952616a69746861253246636f76696467726170682f626c61636b"&gt;&lt;img src="https://camo.githubusercontent.com/ff52b39ecceafe71aa63efe4688cd34f03eeb88b96b860cb34d86508717f54bf/68747470733a2f2f62616467656e2e6e65742f62616467652f2545322539362542322532304465706c6f79253230746f2532304e6f772f242532306e6f7725323052697a6b7952616a69746861253246636f76696467726170682f626c61636b" alt="Deploy to Now"&gt;&lt;/a&gt;
&lt;br&gt;
&lt;a href="https://covidlk.now.sh/" rel="nofollow"&gt;https://covidlk.now.sh/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://covidlk.netlify.app/" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/8a8cdf3ecfadcdead84af6c2223e4497fde20c90ec65d82d8661314a15144e6f/68747470733a2f2f6170692e6e65746c6966792e636f6d2f6170692f76312f6261646765732f34303933393061302d393035332d343537392d613562662d3039633634343366643738362f6465706c6f792d737461747573" alt="Netlify Status"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;br&gt;
&lt;p&gt;This project is a representation of data about an ongoing viral pandemic of coronavirus disease 2019 (COVID-19) in Sri Lanka.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://reactjs.org/" rel="nofollow"&gt;Reactjs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nodejs.org/en/" rel="nofollow"&gt;Nodejs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.mongodb.com/" rel="nofollow"&gt;mongoDB&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/lambda/" rel="nofollow"&gt;AWS Lamda&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nivo.rocks/line/" rel="nofollow"&gt;Nivo Graph library &lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
Update 3 / 5 /2020&lt;/h2&gt;
&lt;h3&gt;
moved to preact for better performance . now supports PWA&lt;/h3&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://raw.githubusercontent.com/preactjs/preact/8b0bcc927995c188eca83cba30fbc83491cc0b2f/logo.svg?sanitize=true"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--L43riXwW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/preactjs/preact/8b0bcc927995c188eca83cba30fbc83491cc0b2f/logo.svg%3Fsanitize%3Dtrue" alt="preact logo"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://preactjs.com/" rel="nofollow"&gt;Preactjs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
added stacked view&lt;/h3&gt;
&lt;h3&gt;
added time range&lt;/h3&gt;
&lt;h2&gt;
Reference + Sources&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://hpb.health.gov.lk/en" rel="nofollow"&gt;https://hpb.health.gov.lk/en&lt;/a&gt; &lt;br&gt;
&lt;a href="http://www.epid.gov.lk/web/index.php?option=com_content&amp;amp;view=article&amp;amp;id=225&amp;amp;lang=si" rel="nofollow"&gt;http://www.epid.gov.lk/web/index.php?option=com_content&amp;amp;view=article&amp;amp;id=225&amp;amp;lang=si&lt;/a&gt; &lt;br&gt;
&lt;a href="https://github.com/arimacdev"&gt;https://github.com/arimacdev&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/RizkyRajitha/covidgraph"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;h2&gt;
  
  
  Thanks for reading till the end  🙌
&lt;/h2&gt;



&lt;h2&gt;
  
  
  I'd love to hear your feedback 💬
&lt;/h2&gt;



&lt;h2&gt;
  
  
  Have a nice day 😎
&lt;/h2&gt;

</description>
      <category>javascript</category>
      <category>react</category>
    </item>
  </channel>
</rss>
