# Mailing List

Make a mailing list subscription page with Node Express. Deployed on Heroku.

Check final product here! (opens new window)

  1. Server with Node Express.
  2. Mailchimp API to send mailing information.
  3. Deployed on Heroku.
  4. Configuring subdomain

image-20201216014000695

image-20201216014018538

# Server with Node Express

# #Express #Node

# Flow

  1. npm init

  2. npm i express body-parser request

  3. Build basic setup

    const express = require("express");
    const bodyParser = require("body-parser");
    const request = require("request");
    const path = require("path");
    
    const app = express();
    const port = 3000;
    
    app.use(express.static("public"));
    
    app.listen(port, () => {
      console.log("listening");
    });
    
    app.get("/", (req, res) => {
      res.sendFile(path.join(__dirname, "/signup.html"));
    });
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
  1. Static Files

image-20201216014104443

CSS and image file is gone when we check on localhost:3000. This is because those files were local. We need to change them to static files in order to send them.

//index.js
app.use(express.static("public"));
1
2

image-20201216014830117

<link href="css/signup.css" rel="stylesheet" />
1

Change the url to relative url starting from 'Public' folder.

background-image: url("../image/bg.jpg");
1

In case of CSS, starting from the CSS file to destination

@font-face {
  font-family: "Intro";
  src: url("fonts/normal_normal_normal.woff2") format("woff2");
}

@font-face {
  font-family: "Intro";
  src: url("fonts/normal_normal_bold.woff2") format("woff2");
  font-weight: bold;
}
1
2
3
4
5
6
7
8
9
10

font doesn't seem to mind to begin from public folder. Or maybe its cache. Doesnt seem to affect font although I deliberately changed the URL to nonsense. Must check later.

  1. POST and Parse

    app.use(bodyParser.urlencoded({ extended: true }));
    // if you don't use bodyParser `req.body` returns undefined.
    
    app.post("/", (req, res) => {
      console.log(req.body);
      const firstName = req.body.firstName;
      const lastName = req.body.lastName;
      const emailAddress = req.body.emailAddress;
    });
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

# Notes

# #static files #bodyParser

  1. Static files
  2. app.use(bodyParser.urlencoded({extended:true})) What is bodyParser? urlencoded? and extended? Why do we need to install it if it is so fundamental?

# MailChimp API

MailChimp Quickstart (opens new window)

  1. You can control your mail list for marketing purpose.
  2. Lists/Audience means a group. Member/contact means a person.
  3. You can create one list if you're a free tier user, and add members to it.
  4. They have their own package to make things easy npm install @mailchimp/mailchimp_marketing.

# Flow

  1. Use the boilerplate code to check if its ok, but first you need to set "type":"module" at package.json to import stuffs.

    import mailchimp from "@mailchimp/mailchimp_marketing";
    // this part does not work on normal package.
    
    mailchimp.setConfig({
      apiKey: "longnumbersofAPIkey",
      server: "us00",
    });
    
    async function run() {
      const response = await mailchimp.ping.get();
      console.log(response);
    }
    
    run();
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
  2. After testing, you can add to the mailing list with

    const mailchimp = require("@mailchimp/mailchimp_marketing");
    const listId = "You can find it at MailChimp";
    
    mailchimp.setConfig({
      apiKey: "api",
      server: "us00",
    });
    
    app.post("/", (req, res) => {
      console.log(req.body);
      const firstName = req.body.firstName;
      const lastName = req.body.lastName;
      const emailAddress = req.body.emailAddress;
      const subscribingUser = {
        firstName: firstName,
        lastName: lastName,
        email: emailAddress,
      };
    
      async function run() {
        try {
          const response = await mailchimp.lists.addListMember(listId, {
            email_address: subscribingUser.email,
            status: "subscribed",
            merge_fields: {
              FNAME: subscribingUser.firstName,
              LNAME: subscribingUser.lastName,
            },
          });
          console.log(
            `Successfully added contact as an audience member. The contact's id is ${response.id}.`
          );
          res.sendFile(__dirname + "/success.html");
        } catch (error) {
          console.log(error);
          res.sendFile(__dirname + "/failure.html");
        }
      }
    
      run();
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41

# Notes

# #POP #IMAP #SMTP #async #await #asynchronous #promise #callback

  1. What is POP and IMAP SMTP?

  2. AsyncFunction

    Newer way to implement promise and callback functions.

    function resolveAfter2Seconds() {
      return new Promise((resolve) => {
        setTimeout(() => {
          resolve("resolved");
        }, 2000);
      });
    }
    
    async function asyncCall() {
      console.log("calling");
      const result = await resolveAfter2Seconds();
      console.log(result);
      // expected output: "resolved"
    }
    
    asyncCall();
    
    //Using await inside aync function allows you to write cleaner style promises
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    //How to use then/catch -> try/catch
    
    async function logTodoTitle() {
      try {
        var user = await fetchUser();
        if (user.id === 1) {
          var todo = await fetchTodo();
          console.log(todo.title);
        }
      } catch (error) {
        console.log(error);
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
  3. Promise

    “A promise is an object that may produce a single value some time in the future” . An object used for asynchronous .

  4. Asynchronous / Callback

# Deploy on Heroku

Cloud platform as a service (Paas) started in 2007, initially only supported Ruby. Now supports Java, Node.js, Scala, Clojure, Python, PHP and Go. Acquired by Salesforce in 2012. The name "Heroku" is a portmanteau of "heroic" and "haiku". The Japanese theme is a nod to Matz for creating Ruby. Wikipedia (opens new window)

# Flow

  1. First install heroku cli

    npm i -g heroku

  2. Change port

    const port = process.env.PORT;
    
    app.listen(port || 3000, () => {
      console.log("listening");
    });
    //The || lets you allow locally and deployed
    
    1
    2
    3
    4
    5
    6
  1. Create procfile

    image-20201216153443140

    //Procfile
    web: node index.js
    
    1
    2

    This step might not be necessary anymore.

  2. You can either push directly to heroku or use github to deploy. I'll try using github.

  3. Make repository and push to github. Go to heroku app and integrate with repository, deployment is easy but

# .env files and API_KEYS

# #.env

As soon as I pushed, my API_KEYS which were included in the index.js file was compromised and disabled. I should never do this again. I had to find a way around.

npm i dotenv

require("dotenv").config();

mailchimp.setConfig({
  apiKey: process.env.API_KEY,
  server: "us18",
});
1
2
3
4
5
6
// .env file
API_KEY = "98c71dummydatacc43bb3164354e-us18";
1
2

but how to use it deployed?

First try

image-20201216163102539

Error: Unauthorized
1

Second try

image-20201216163444271

Finally works!!!

image-20201216163808066

image-20201216164854672

Sent my first newsletter to my friends!

# Configuring my subdomain to use on Heroku appp

# #subdomain #cname

You can make subdomains and give to independent websites. ex) blog.keithkwon.dev, newsletter.keithkwon.dev ...I configured so that newsletter.keithkwon.dev will point to my new heroku app

stackoverflow (opens new window)

gabebw (opens new window)

# Flow

  1. Upgrade to paid user in Heroku, necessary for auto SSL.

  2. Allow subdomain by Heroku console.

    image-20201216233551821

    heroku domains:add cool.tutorial.com
    
    1

    May not be necessary.

  1. Register for auto SSL on Heroku and add custom domain

    image-20201216233624102

  2. Configure CNAME on google domain

    image-20201216233852029

# Notes

  1. What is Redis? What is PostgreSQL?
  2. What does process.env have?
  3. What is a websocket?
  4. How to use .env files
  5. What is github actions?
  6. About Hosting : ANAME CNAME DNS SSL Synthetic Records, Custom Resource Records, DNSSEC, NAME SERVERS
Last Updated: 3/1/2021, 9:19:08 PM