Ravindra BagaleCourses & study guides मराठी

Chapter 73: DevOps Projects Part 3: Docker Websites, APIs, Compose, Registry and ECR

73.2 Project 18: Dockerize a Node.js API

Friends, Node APIs ship in containers every day. Today we write a tiny Express API, a Dockerfile, build the image, run it on port 3000, and prove /health with curl. Pin node:20-alpine. Copy package files before app code so Docker cache stays warm. Prove /health with curl before you celebrate.

What you will build in this project

  1. A tiny Express app with /health.
  2. Dockerfile with pinned node Alpine.
  3. Image node-api:1.0 running on 3000.
  4. curl proves health and a sample route.

1. Start from a real company story

At Flipkart in Bengaluru, the cart API was copied as zip files onto six VMs. One VM had an old Node 14 binary and crashed on a new optional chaining line. The ops lead asked every engineer to paste the exact docker images size line into the pull request before merge. That one habit cut surprise production rollbacks. The on-call chart now shows image size next to deploy time. New joiners pair on the health curl before they touch Compose.

Peak hour errors cost about ₹18 lakh in abandoned carts that evening.

Arjun put the API in a node:20-alpine image. The same tag ran everywhere. Rollback was the previous tag.

Today we dockerize that Node API the same way.

app.js + package.jsonExpress /healthDockerfileFROM node:20-alpinedocker buildnode-api:1.0run -p 3000:3000curl /health
Picture 1: from zip copy to Node container

Ravindra Bagale's Tip

Many students use FROM node:latest. Pin node:20-alpine so builds stay repeatable. Keep that habit. Write every command in your notebook. When something fails, copy the exact error line before you change anything else. Measure twice, change once. Never use node:latest in a job that ships to customers. When npm ci fails, read the lockfile mismatch line first. Keep a notebook of image sizes after every successful build. Write the three curl checks in your notebook before you close the laptop for the day.

2. Start and prepare

Step 1: Make app folder

Why: Need a workspace. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.

What: mkdir. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.

Example 1: mkdir

Type this:

mkdir -p /tmp/node-api && cd /tmp/node-api && echo ready

You will see:

ready

What it means: Folder ready.

Example 2: pwd

Type this:

pwd

You will see:

/tmp/node-api

What it means: In place.

Example 3: empty

Type this:

ls | wc -l

You will see:

0

What it means: Fresh.

Step 2: Write package.json

Why: Declare express. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.

What: printf package.json. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.

Example 4: pkg

Type this:

printf '%s\n' '{' '  "name": "node-api",' '  "version": "1.0.0",' '  "main": "app.js",' '  "dependencies": { "express": "4.21.0" }' '}' > package.json && cat package.json

You will see:

{ name node-api ... express 4.21.0 }

What it means: Pinned express.

Example 5: name

Type this:

python3 -c 'import json;print(json.load(open("package.json"))["name"])'

You will see:

node-api

What it means: Parsed.

Example 6: mode

Type this:

stat -c '%a' package.json

You will see:

644

What it means: Safe.

Step 3: Write app.js

Why: Two routes. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.

What: printf app.js. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.

Example 7: app

Type this:

printf '%s\n' "const express=require('express');" 'const app=express();' "app.get('/health',(_,res)=>res.json({ok:true}));" "app.get('/api/hello',(_,res)=>res.json({msg:'cart-api'}));" 'app.listen(3000,()=>console.log("listen 3000"));' > app.js && wc -l app.js

You will see:

5 app.js

What it means: Tiny API.

Example 8: grep health

Type this:

grep health app.js

You will see:

app.get('/health'...

What it means: Route present.

Example 9: grep hello

Type this:

grep hello app.js

You will see:

app.get('/api/hello'...

What it means: Route present.

3. Build the main pieces

Step 4: Prove node locally

Why: Local sanity. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.

What: node check. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.

Example 10: npm note

Type this:

echo 'In real lab: npm install express@4.21.0 --save'

You will see:

In real lab: npm install express@4.21.0 --save

What it means: Install deps before local run.

Example 11: syntax

Type this:

node --check app.js && echo syntax_ok

You will see:

syntax_ok

What it means: JS parses.

Example 12: node ver

Type this:

node -v

You will see:

v20.17.0

What it means: Local Node near image major.

Step 5: Write Dockerfile

Why: Pin Alpine. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.

What: printf Dockerfile. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.

Example 13: df

Type this:

printf '%s\n' 'FROM node:20-alpine' 'WORKDIR /app' 'COPY package.json ./' 'RUN npm install --omit=dev' 'COPY app.js ./' 'EXPOSE 3000' 'USER node' 'CMD ["node","app.js"]' > Dockerfile && cat Dockerfile

You will see:

FROM node:20-alpine
WORKDIR /app
...

What it means: Pinned + non-root.

Example 14: from

Type this:

head -1 Dockerfile

You will see:

FROM node:20-alpine

What it means: Pin.

Example 15: user

Type this:

grep USER Dockerfile

You will see:

USER node

What it means: Non-root.

Step 6: Write dockerignore

Why: Lean context. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.

What: printf ignore. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.

Example 16: ignore

Type this:

printf '%s\n' 'node_modules' '.git' '*.md' > .dockerignore && cat .dockerignore

You will see:

node_modules
.git
*.md

What it means: Lean context.

Example 17: ls

Type this:

ls /tmp/node-api

You will see:

Dockerfile
app.js
package.json
.dockerignore

What it means: Ready.

Example 18: wc

Type this:

wc -l Dockerfile

You will see:

8 Dockerfile

What it means: Short.

4. Automate and verify

Step 7: Build image

Why: docker build. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.

What: docker build. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.

Example 19: build

Type this:

docker build -t node-api:1.0 /tmp/node-api

You will see:

Successfully tagged node-api:1.0

What it means: Built.

Example 20: images

Type this:

docker images node-api --format '{{.Repository}}:{{.Tag}} {{.Size}}'

You will see:

node-api:1.0 50MB

What it means: Listed.

Example 21: history

Type this:

docker history node-api:1.0 | head -4

You will see:

IMAGE CREATED CREATED BY SIZE

What it means: Layers.

Step 8: Run container

Why: docker run -d. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.

What: docker run. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.

Example 22: run

Type this:

docker rm -f node-api >/dev/null 2>&1; docker run -d --name node-api -p 3000:3000 node-api:1.0 && echo started

You will see:

started

What it means: Detached.

Example 23: ps

Type this:

docker ps --filter name=node-api --format '{{.Names}} {{.Status}} {{.Ports}}'

You will see:

node-api Up ... 0.0.0.0:3000->3000/tcp

What it means: Mapped.

Example 24: wait

Type this:

sleep 1; echo up

You will see:

up

What it means: Ready.

Step 9: curl health

Why: /health 200. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.

What: curl. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.

Example 25: health

Type this:

curl -s http://127.0.0.1:3000/health

You will see:

{"ok":true}

What it means: Health OK.

Example 26: code

Type this:

curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:3000/health

You will see:

200

What it means: HTTP 200.

Example 27: ctype

Type this:

curl -sI http://127.0.0.1:3000/health | head -2

You will see:

HTTP/1.1 200 OK
Content-Type: application/json

What it means: JSON.

5. Harden and accept

Step 10: curl route

Why: /api/hello. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.

What: curl. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.

Example 28: hello

Type this:

curl -s http://127.0.0.1:3000/api/hello

You will see:

{"msg":"cart-api"}

What it means: Route OK.

Example 29: 404

Type this:

curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:3000/nope

You will see:

404

What it means: Missing path.

Example 30: headers

Type this:

curl -sI http://127.0.0.1:3000/api/hello | grep -i content-type

You will see:

Content-Type: application/json

What it means: JSON.

Step 11: Logs and exec

Why: logs prove. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.

What: docker logs. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.

Example 31: logs

Type this:

docker logs node-api 2>&1 | tail -3

You will see:

listen 3000

What it means: Started.

Example 32: exec

Type this:

docker exec node-api node -v

You will see:

v20.17.0

What it means: Inside matches pin.

Example 33: who

Type this:

docker exec node-api whoami

You will see:

node

What it means: Non-root.

Step 12: Acceptance checklist

Why: Arjun signs off when these pass. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.

What: All green. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.

Example 34: smoke

Type this:

curl -s http://127.0.0.1:3000/health | grep -q ok && echo pass

You will see:

pass

What it means: Smoke.

Example 35: no 777

Type this:

stat -c '%a' /tmp/node-api/Dockerfile

You will see:

644

What it means: Safe mode.

Example 36: Sign-off

Type this:

echo 'Arjun: PASS'

You will see:

Arjun: PASS

What it means: Done.

Check yourself

Q1. Base image Arjun used?

node:20-alpine

Q2. Host port?

3000

Q3. Health path?

/health

Q4. Who fixed Flipkart cart API?

Arjun

Q5. Build command?

docker build

Q6. Why pin the tag?

So builds stay repeatable

Friends, you dockerized a Node API with a pinned Alpine image. Did you understand? Let us try it once more. Change the health body, rebuild as 1.1, and switch the container.

Lab: try at home

  1. Write package.json and app.js.
  2. Write Dockerfile and .dockerignore.
  3. Build node-api:1.0.
  4. Run on 3000 and curl.
  5. Tag 1.1 after a text change.

Recap

  • Pin the Node major.
  • COPY package files before code for cache.
  • npm ci --omit=dev for prod.
  • -p maps host to container.
  • HEALTHCHECK optional but useful.