DEV Community

Cover image for How to build a Node.js eCommerce website for free
Adrian Mejia
Adrian Mejia

Posted on • Originally published at on

How to build a Node.js eCommerce website for free

Running an online store that sells digital goods is easier than ever. Thanks to generous free plans for developers, you don’t have to spend a dime to run your e-commerce site for a decent amount of users. In this post, I’ll go over how I put together to sell my eBook.

A 10,000-feet view description would be something like this:

Liquid error: internal

TL; DR: The e-Commerce site final stack is the following:

  • Node.js (Backend processing: payment webhooks)
  • Stripe (Payment gateway)
  • Heroku (Run server code)
  • Netlify (Host static files)
  • Amazon S3 (Host assets)
  • CircleCI (Test code and generate assets)
  • Mailgun (emails platform)

This diagram shows how each part interacts with each other:

nodejs e-commerce app

Automating the generation of the assets (PDF)

I have Github repository where the book docs and code live:

GitHub logo amejiarosario / dsa.js-data-structures-algorithms-javascript

🥞Data Structures and Algorithms explained and implemented in JavaScript + eBook


Data Structures and Algorithms in JavaScript

CircleCI NPM version chat

This is the coding implementations of the DSA.js book and the repo for the NPM package.

In this repository, you can find the implementation of algorithms and data structures in JavaScript. This material can be used as a reference manual for developers, or you can refresh specific topics before an interview. Also, you can find ideas to solve problems more efficiently.

Interactive Data Structures

Table of Contents


You can clone the repo or install the code from NPM:

npm install dsa.js
Enter fullscreen mode Exit fullscreen mode

and then you can import it into your programs or CLI

const { LinkedList, Queue, Stack } = require('dsa.js');
Enter fullscreen mode Exit fullscreen mode

For a full list of all the exposed data structures and algorithms see.


Algorithms are an…

Every time I made a change (or somebody in the community), it triggers some process on CI that run all tests and generate a new updated document and store it AWS S3.

Generating assets automatically is useful because I want every buyer to get the latest copy.

Hosting e-Commerce site

I always want to try out new JavaScript/CSS frameworks. However, I resisted the temptation and asked my self: Does a page for selling a book need to be dynamic? Nope. So, it will be more performant if I use plain old CSS and HTML. That’s what I did. Static pages also have the advantage that can be cached and served from a CDN.

I used Netlify to host the static website for free. One single git push will update the site on the domain name of choice (e.g. It also uses a global CDN so your page loads faster from anywhere in the world!

Processing Payments

The next part is to add a Buy button. Stripe provides a helpful checkout page that they host themselves and take care of the PCI compliance when dealing with credit cards. So, I used that, and they process the payment for me.

But how do I know if the customer bought my book or got distracted? For that, I need a server that listens for a payment webhook. In the Stripe configuration page, you tell them to send a POST request (webhook) with the customer information when a particular event.

Here is the code for a simple webhook server

const express = require('express');
const bodyParser = require('body-parser');

const app = express();
const port = process.env.PORT || 5000;


app.listen(port, () => {
  console.log(`Listening for webhooks: http://localhost:${port}`);
});'/webhook', async (req, res) => {
  const event = req.body;


  if (event.type === 'payment_intent.succeeded') {
    // TODO: send event to RabbitMQ instead of generating the PDF here.
    // It's not good practice to block a request handler with long processes
    const { sendPdfToBuyer } = require('./process-pdf');

// all other routes, prevent node crashing for undefined routes
app.route('*', async (req, res) => {
  res.json({ ok: 1 });
Enter fullscreen mode Exit fullscreen mode

And that brings us to the next part, the Node.js server to take care of the rest.

Backend processing

I created a Node.js server that listened for webhook requests. When a customer paid for the book an event with the details is sent to this server, and the document pipeline is kicked off.

The server first downloads the book from AWS S3 bucket, where the latest raw document is. Later, the server uses a library that allows to manipulate the PDF and add the buyer’s stamp on the eBook. Finally, the material is attached to and send through email.

async function sendPdfToBuyer(webhookEvent) {
  const email = =>', ');
  const pdfUrl = await getLatestPdfUrl();
  const fileName = pdfUrl.split('/').pop();
  const pdfBuffer = await downloadPdf(pdfUrl);
  const stampedPdfPath = await stampedPdfWithBuyerData({ pdfBuffer, email, fileName });
  await sendEmail({ stampedPdfPath, email, fileName });
Enter fullscreen mode Exit fullscreen mode

Sending emails

Sending emails was a little trickier than I thought.

DNS settings and authentication

First, I was using my domain name, so I have to set up the DNS settings to make it work. However, I notice all my test emails to myself ended up on the junk mail.

Reading more about the topic I realized that I have to authenticate emails using SPF and DKIM, I still don’t know what they are in details, but they allow email providers (Gmail, Yahoo) to verify you are who you say you are. They are setup also using DNS settings given by the emailing service provides.

I set up the setting initially with Sendgrid but was still getting my emails to the junk folder. I moved to Mailgun and got better results. For some reason, would always reject the emails. As I learned unless you pay for a dedicated IP address the email service provider would use a “shared” IP in many accounts. If for some reason the IP gets a bad reputation then your emails will go to spam folder even if you have never sent an email before! I got this fixed by opening a support ticket and after they changed the IP it was working fine with any address.

Email Templates

The final part related to emails is doing a template. I have never done it before. The difference between HTML for email templates and web pages HTML is that on the email you should embed everything into the message itself. Spam filters don’t like external link loading additional resources. So, every CSS should be inline and has to also be responsible.

Well, there you have it: an e-commerce store that collects the payments and sends digital goods to buyers. Let’s close talking about the cost of maintenance.

Cost of running the e-Commerce store

This is the breakdown of the monthly costs:

  • Hosting static websites: $0 (if you use Netlify or Github pages)
  • Payment Gateway: $0 (Stripe will only a 2.9% charge if you sell something otherwise $0)
  • Node.js server: $0 (Heroku, AWS, Google Cloud and many others have a free plan for developers)
  • Email Service: $0 (Mailgun and Sendgrid both have free plans. The former allows you to send 10K emails per month)

The total is: $0 / mo.

Note: Like any website, If you want to use a custom domain as I do, you have to pay for it which is about $1/mo.

Top comments (10)

vagoel profile image

Hats off Adrian for your work, loved the concept. The illustration is so helpful to understand complete flow of your project. Definitely a great learning source for devs like me to understand how different process are gelled with each other.

amejiarosario profile image
Adrian Mejia

Thanks a lot! The illustration took me longer than writing the rest of the post 😅 but I think it was worth it

hyaovi profile image

The way you explained each stack is very neat. Great article

realabbas profile image
Ali Abbas

A handy amount of stuff can be learnt from this project. Like MailGun, CircleCI, Amazon S3 and webhooks. Great post!

amejiarosario profile image
Adrian Mejia

Yes, they can be learn as you go. I played a little bit with each piece individually and when I understood then I put it work together.

kienngo profile image

Very informative! Thanks for the article <3

florinpop17 profile image
Florin Pop

Very interesting! Thanks for sharing it with us!

edper profile image

Adrian thank you for this wonderful learning resource. But I wonder where did you get your $1/month domain? Thanks.

amejiarosario profile image
Adrian Mejia

I'm currently using Google domains. I was using GoDaddy before they started cheaper around $12/year and the following years went to $18. I didn't like that increase so I moved to Google domains that is $12 flat every year (or $1 month).

edper profile image

Thank you for the reply Adrian. I checked on Google before and they were a little bit expensive and never checked there again. I normally checked on GoDaddy although normally they are pricey as well. But anyway, thank you for your helpful reply.