I am trying to construct a node application with Nodemon as a live-server for the application. It is a simple newsletter signup web app that uses Mailchimp API to store the list of people that have signed up.
In my app.js, I have used edited versions of template code from the Mailchimp developer guides, and I have discovered that async/await function I am using is having issues posting the data to the Mailchimp sever.
Below is a link to the article in the Mailchimp developer guides : text
Below is my code in app.js:
//Requiring mailchimp's module
//For this we need to install the npm module @mailchimp/mailchimp_marketing. To do that we write:
//npm install @mailchimp/mailchimp_marketing
const mailchimp = require("@mailchimp/mailchimp_marketing");
//Requiring express and body parser and initializing the constant "app"
const express = require("express");
const bodyParser = require("body-parser");
const app = express();
//Using body-parser
app.use(bodyParser.urlencoded({extended:true}));
//The public folder which holds the CSS
app.use(express.static("public"));
//Listening on port 3000 and if it goes well then logging a message saying that the server is running
app.listen(process.env.PORT||3000,function () {
console.log("Server is running at port 3000");
});
//Sending the signup.html file to the browser as soon as a request is made on localhost:3000
app.get("/", function (req, res) {
res.sendFile(__dirname + "/signup.html");
});
//Setting up MailChimp
mailchimp.setConfig({
//*****************************ENTER YOUR API KEY HERE******************************
apiKey: "7825a331ceaec9fa7c606108d9eee46d-us21",
//*****************************ENTER YOUR API KEY PREFIX HERE i.e.THE SERVER******************************
server: "us21"
});
//As soon as the sign in button is pressed execute this
app.post("/", function (req,res) {
//*****************************CHANGE THIS ACCORDING TO THE VALUES YOU HAVE ENTERED IN THE INPUT ATTRIBUTE IN HTML******************************
const firstName = req.body.fName;
const lastName = req.body.lName;
const email = req.body.email;
//*****************************ENTER YOU LIST ID HERE******************************
const listId = "2fdb4c478c";
//Creating an object with the users data
const subscribingUser = {
firstName: firstName,
lastName: lastName,
email: email
};
//Uploading the data to the server
async function run() {
const response = await mailchimp.lists.addListMember(listId, {
email_address: subscribingUser.email,
status: "subscribed",
merge_fields: {
FNAME: subscribingUser.firstName,
LNAME: subscribingUser.lastName
}
});
//If all goes well logging the contact's id
res.sendFile(__dirname + "/success.html")
console.log(
`Successfully added contact as an audience member. The contact's id is ${
response.id
}.`
);
}
//Running the function and catching the errors (if any)
// So the catch statement is executed when there is an error so if anything goes wrong the code in the catch code is executed. In the catch block we're sending back the failure page. This means if anything goes wrong send the faliure page
run().catch(e => res.sendFile(__dirname + "/failure.html"));
});
And my signup.html page, containing the structure of the input forms(note: It uses a template from bootstrap and only the form is of importance):
<!doctype html>
<html lang="en" data-bs-theme="auto">
<head><script src="/docs/5.3/assets/js/color-modes.js"></script>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="Mark Otto, Jacob Thornton, and Bootstrap contributors">
<meta name="generator" content="Hugo 0.111.3">
<title>Newsletter Signup</title>
<link rel="canonical" href="https://getbootstrap.com/docs/5.3/examples/sign-in/">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-KK94CHFLLe+nY2dmCWGMq91rCGa5gtU4mk92HdvYe+M/SXH301p5ILy+dN9+nJOZ" crossorigin="anonymous">
<!-- Favicons -->
<link rel="apple-touch-icon" href="/docs/5.3/assets/img/favicons/apple-touch-icon.png" sizes="180x180">
<link rel="icon" href="/docs/5.3/assets/img/favicons/favicon-32x32.png" sizes="32x32" type="image/png">
<link rel="icon" href="/docs/5.3/assets/img/favicons/favicon-16x16.png" sizes="16x16" type="image/png">
<link rel="manifest" href="/docs/5.3/assets/img/favicons/manifest.json">
<link rel="mask-icon" href="/docs/5.3/assets/img/favicons/safari-pinned-tab.svg" color="#712cf9">
<link rel="icon" href="/docs/5.3/assets/img/favicons/favicon.ico">
<meta name="theme-color" content="#712cf9">
<style>
.bd-placeholder-img {
font-size: 1.125rem;
text-anchor: middle;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
}
@media (min-width: 768px) {
.bd-placeholder-img-lg {
font-size: 3.5rem;
}
}
.b-example-divider {
width: 100%;
height: 3rem;
background-color: rgba(0, 0, 0, .1);
border: solid rgba(0, 0, 0, .15);
border-width: 1px 0;
box-shadow: inset 0 .5em 1.5em rgba(0, 0, 0, .1), inset 0 .125em .5em rgba(0, 0, 0, .15);
}
.b-example-vr {
flex-shrink: 0;
width: 1.5rem;
height: 100vh;
}
.bi {
vertical-align: -.125em;
fill: currentColor;
}
.nav-scroller {
position: relative;
z-index: 2;
height: 2.75rem;
overflow-y: hidden;
}
.nav-scroller .nav {
display: flex;
flex-wrap: nowrap;
padding-bottom: 1rem;
margin-top: -1px;
overflow-x: auto;
text-align: center;
white-space: nowrap;
-webkit-overflow-scrolling: touch;
}
.btn-bd-primary {
--bd-violet-bg: #712cf9;
--bd-violet-rgb: 112.520718, 44.062154, 249.437846;
--bs-btn-font-weight: 600;
--bs-btn-color: var(--bs-white);
--bs-btn-bg: var(--bd-violet-bg);
--bs-btn-border-color: var(--bd-violet-bg);
--bs-btn-hover-color: var(--bs-white);
--bs-btn-hover-bg: #6528e0;
--bs-btn-hover-border-color: #6528e0;
--bs-btn-focus-shadow-rgb: var(--bd-violet-rgb);
--bs-btn-active-color: var(--bs-btn-hover-color);
--bs-btn-active-bg: #5a23c8;
--bs-btn-active-border-color: #5a23c8;
}
.bd-mode-toggle {
z-index: 1500;
}
</style>
<!-- Custom styles for this template -->
<link href="/css/style.css" rel="stylesheet">
</head>
<body class="text-center">
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<symbol id="check2" viewBox="0 0 16 16">
<path d="M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0z"/>
</symbol>
<symbol id="circle-half" viewBox="0 0 16 16">
<path d="M8 15A7 7 0 1 0 8 1v14zm0 1A8 8 0 1 1 8 0a8 8 0 0 1 0 16z"/>
</symbol>
<symbol id="moon-stars-fill" viewBox="0 0 16 16">
<path d="M6 .278a.768.768 0 0 1 .08.858 7.208 7.208 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277.527 0 1.04-.055 1.533-.16a.787.787 0 0 1 .81.316.733.733 0 0 1-.031.893A8.349 8.349 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.752.752 0 0 1 6 .278z"/>
<path d="M10.794 3.148a.217.217 0 0 1 .412 0l.387 1.162c.173.518.579.924 1.097 1.097l1.162.387a.217.217 0 0 1 0 .412l-1.162.387a1.734 1.734 0 0 0-1.097 1.097l-.387 1.162a.217.217 0 0 1-.412 0l-.387-1.162A1.734 1.734 0 0 0 9.31 6.593l-1.162-.387a.217.217 0 0 1 0-.412l1.162-.387a1.734 1.734 0 0 0 1.097-1.097l.387-1.162zM13.863.099a.145.145 0 0 1 .274 0l.258.774c.115.346.386.617.732.732l.774.258a.145.145 0 0 1 0 .274l-.774.258a1.156 1.156 0 0 0-.732.732l-.258.774a.145.145 0 0 1-.274 0l-.258-.774a1.156 1.156 0 0 0-.732-.732l-.774-.258a.145.145 0 0 1 0-.274l.774-.258c.346-.115.617-.386.732-.732L13.863.1z"/>
</symbol>
<symbol id="sun-fill" viewBox="0 0 16 16">
<path d="M8 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0zm0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13zm8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5zM3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8zm10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0zm-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0zm9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707zM4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .708z"/>
</symbol>
</svg>
<div class="dropdown position-fixed bottom-0 end-0 mb-3 me-3 bd-mode-toggle">
<button class="btn btn-bd-primary py-2 dropdown-toggle d-flex align-items-center"
id="bd-theme"
type="button"
aria-expanded="false"
data-bs-toggle="dropdown"
aria-label="Toggle theme (auto)">
<svg class="bi my-1 theme-icon-active" width="1em" height="1em"><use href="#circle-half"></use></svg>
<span class="visually-hidden" id="bd-theme-text">Toggle theme</span>
</button>
<ul class="dropdown-menu dropdown-menu-end shadow" aria-labelledby="bd-theme-text">
<li>
<button type="button" class="dropdown-item d-flex align-items-center" data-bs-theme-value="light" aria-pressed="false">
<svg class="bi me-2 opacity-50 theme-icon" width="1em" height="1em"><use href="#sun-fill"></use></svg>
Light
<svg class="bi ms-auto d-none" width="1em" height="1em"><use href="#check2"></use></svg>
</button>
</li>
<li>
<button type="button" class="dropdown-item d-flex align-items-center" data-bs-theme-value="dark" aria-pressed="false">
<svg class="bi me-2 opacity-50 theme-icon" width="1em" height="1em"><use href="#moon-stars-fill"></use></svg>
Dark
<svg class="bi ms-auto d-none" width="1em" height="1em"><use href="#check2"></use></svg>
</button>
</li>
<li>
<button type="button" class="dropdown-item d-flex align-items-center active" data-bs-theme-value="auto" aria-pressed="true">
<svg class="bi me-2 opacity-50 theme-icon" width="1em" height="1em"><use href="#circle-half"></use></svg>
Auto
<svg class="bi ms-auto d-none" width="1em" height="1em"><use href="#check2"></use></svg>
</button>
</li>
</ul>
</div>
<main class="form-signin w-100 m-auto">
<form action="/" method="POST">
<img class="mb-4" src="/docs/5.3/assets/brand/bootstrap-logo.svg" alt="" width="72" height="57">
<h1 class="h3 mb-3 fw-normal">Sign Up To My Newsletter</h1>
<input type="text" name="fName" class="form-control top" placeholder="First Name" required autofocus>
<input type="text" name="lName" class="form-control middle" placeholder="Last Name" required>
<input type="email" name="email" class="form-control bottom" placeholder="Email" required>
<button class="w-100 btn btn-lg btn-primary" type="submit">Sign Me up!</button>
<p class="mt-5 mb-3 text-body-secondary">© A kjoker production</p>
</form>
</main>
</body>
</html>
I have two pages for the client to be directed to upon entering their data, when ran correctly they are to be directed to a success page and their data is to be uploaded to the Mailchimp server but upon starting the live-server and clicking the signup button with the above code, I am redirected to the failure page and no change is made to the terminal where Nodemon has started(It is to display response.id). When I try and call the async/await function run() directly below the code block:
console.log(
`Successfully added contact as an audience member. The contact's id is ${
response.id
}.`
and then click the button, I am directed to the failure page again, and the terminal with Nodemon displays a long block of code errors until the Nodemon server crashes. A block of the code error:
header: {
server: 'openresty',
'content-type': 'application/problem+json; charset=utf-8',
'x-request-id': 'f3a563bb-dd44-b74d-5205-85cfc4177baf',
link: '<https://us21.api.mailchimp.com/schema/3.0/Definitions/ProblemDetailDocument.json>; rel="describedBy"',
'content-encoding': 'gzip',
vary: 'Accept-Encoding',
'content-length': '213',
date: 'Mon, 19 Jun 2023 22:08:54 GMT',
connection: 'close'
},
statusCode: 401,
status: 401,
statusType: 4,
info: false,
ok: false,
redirect: false,
clientError: true,
serverError: false,
error: Error: cannot POST /3.0/lists/2fdb4c478c/members (401)
at Response.toError (C:\Users\danie\OneDrive\Newsletter-Signup\node_modules\superagent\lib\node\response.js:94:15)
at ResponseBase._setStatusProperties (C:\Users\danie\OneDrive\Newsletter-Signup\node_modules\superagent\lib\response-base.js:123:16)
at new Response (C:\Users\danie\OneDrive\Newsletter-Signup\node_modules\superagent\lib\node\response.js:41:8)
at Request._emitResponse (C:\Users\danie\OneDrive\Newsletter-Signup\node_modules\superagent\lib\node\index.js:739:20)
at C:\Users\danie\OneDrive\Newsletter-Signup\node_modules\superagent\lib\node\index.js:903:38
at Stream.<anonymous> (C:\Users\danie\OneDrive\Newsletter-Signup\node_modules\superagent\lib\node\parsers\json.js:19:7)
at Stream.emit (node:events:513:28)
at Unzip.<anonymous> (C:\Users\danie\OneDrive\Newsletter-Signup\node_modules\superagent\lib\node\unzip.js:55:12)
at Unzip.emit (node:events:513:28)
at endReadableNT (node:internal/streams/readable:1359:12) {
status: 401,
text: `{"type":"https://mailchimp.com/developer/marketing/docs/errors/","title":"API Key Invalid","status":401,"detail":"Your API key may be invalid, or you've attempted to access the wrong datacenter.","instance":"f3a563bb-dd44-b74d-5205-85cfc4177baf"}`,
method: 'POST',
path: '/3.0/lists/2fdb4c478c/members'
},
accepted: false,
noContent: false,
badRequest: false,
unauthorized: true,
notAcceptable: false,
forbidden: false,
notFound: false,
type: 'application/problem+json',
charset: 'utf-8',
links: {
describedBy: 'https://us21.api.mailchimp.com/schema/3.0/Definitions/ProblemDetailDocument.json'
},
setEncoding: [Function: bound ],
redirects: [],
[Symbol(kCapture)]: false
}
}
Node.js v18.15.0
[nodemon] app crashed - waiting for file changes before starting...
The API key and listID provided should be valid, but the error seems to point to that. Because the server has crashed reloading it displays the error 401.
Per their documentation, a 401 error means "API Key Invalid". And, the error also shows unauthorized: true
and clientError: true
which are more indications that your API Key is not correct.
So, this:
//Setting up MailChimp
mailchimp.setConfig({
apiKey: "7825a331ceaec9fa7c606108d9eee46d-us21",
server: "us21"
});
is apparently not correct. As others have said, it's possible that the -us21
should not be on the apiKey. Or perhaps you don't have the right server
specified. In either case, this is the line of code that needs to be fixed to address the 401 error.
You need to login to your mailchimp developer account and again look at what your apiKey and server are supposed to be. We can't do that for you - that's something you will have to do.