π₯ 1. ARCHITECTURE (REAL FLOW)
Client β /login β JWT token
β /protected β validate token
β Newman tests β CI/CD validation
π₯ 2. PROJECT STRUCTURE
jwt-newman-lab/
βββ app/
β βββ server.js
βββ Dockerfile
βββ package.json
βββ collection.json
βββ environment.json
βββ .github/
β βββ workflows/
β βββ api-test.yml
π₯ 3. STEP 1 β SECURE API WITH JWT
π app/server.js
const express = require("express");
const jwt = require("jsonwebtoken");
const app = express();
app.use(express.json());
// π Secret from ENV (IMPORTANT)
const SECRET = process.env.JWT_SECRET || "dev-secret";
// LOGIN
app.post("/login", (req, res) => {
const { username } = req.body;
if (!username) {
return res.status(400).json({ error: "Username required" });
}
const token = jwt.sign({ username }, SECRET, { expiresIn: "1h" });
res.json({ token });
});
// PROTECTED ROUTE
app.get("/protected", (req, res) => {
const auth = req.headers["authorization"];
if (!auth) {
return res.status(401).json({ error: "No token" });
}
const token = auth.split(" ")[1];
try {
const decoded = jwt.verify(token, SECRET);
res.json({ message: "Access granted", user: decoded.username });
} catch (err) {
res.status(403).json({ error: "Invalid token" });
}
});
app.listen(3000, () => {
console.log("JWT API running on 3000");
});
π₯ 4. STEP 2 β PACKAGE.JSON
{
"name": "jwt-api",
"version": "1.0.0",
"main": "app/server.js",
"scripts": {
"start": "node app/server.js"
},
"dependencies": {
"express": "^4.18.2",
"jsonwebtoken": "^9.0.0"
}
}
π₯ 5. STEP 3 β DOCKERFILE
FROM node:18
WORKDIR /app
COPY package.json .
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
π₯ 6. STEP 4 β POSTMAN COLLECTION (IMPORTANT)
Request 1 β LOGIN
POST {{base_url}}/login
Body:
{
"username": "aisalkyn"
}
Tests (SAVE TOKEN)
let json = pm.response.json();
pm.environment.set("token", json.token);
pm.test("Token received", function () {
pm.expect(json.token).to.exist;
});
Request 2 β PROTECTED
GET {{base_url}}/protected
Headers:
Authorization: Bearer {{token}}
Tests
pm.test("Access granted", function () {
pm.response.to.have.status(200);
});
π₯ 7. STEP 5 β ENVIRONMENT FILE
{
"name": "local",
"values": [
{ "key": "base_url", "value": "http://localhost:3000" },
{ "key": "token", "value": "" }
]
}
π₯ 8. STEP 6 β RUN LOCALLY
docker build -t jwt-api .
docker run -d -p 3000:3000 -e JWT_SECRET=mysecret jwt-api
newman run collection.json -e environment.json
π₯ 9. STEP 7 β GITHUB ACTIONS (REAL CI/CD)
π .github/workflows/api-test.yml
name: JWT API Test
on:
push:
branches: [ main ]
jobs:
test-api:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build & Run API
run: |
docker build -t jwt-api .
docker run -d -p 3000:3000 -e JWT_SECRET=${{ secrets.JWT_SECRET }} jwt-api
- name: Wait for API
run: sleep 10
- name: Install Newman
run: npm install -g newman
- name: Run Tests
run: newman run collection.json -e environment.json
π₯ 10. π SECRETS (VERY IMPORTANT)
In GitHub:
Settings β Secrets β Actions
Add:
JWT_SECRET = mysupersecret
π₯ 11. WHY THIS IS REAL DEVOPS
You are doing:
- Secure API (JWT)
- No hardcoding secrets
- Pipeline validation
- Auth testing
- Fail-safe deployment
π₯ 12. COMMON FAILURES (INTERVIEW GOLD)
β Token not saved
Newman fails because:
{{token}} is empty
β Wrong secret
Token created β token verified
β Missing Authorization header
401 Unauthorized
β Pipeline too fast
API not ready β tests fail
β Secrets not configured
GitHub:
JWT_SECRET undefined
π₯ 13. WHAT YOU SAY IN INTERVIEW
Strong answer:
"We test authenticated APIs using Postman and Newman. Token is dynamically generated and stored during test execution. In CI/CD, secrets are injected securely using GitHub Actions. This ensures secure and automated API validation before release."
π₯ 14. WHAT YOU REPORT TO MANAGER
Example:
- Auth Test: PASS
- Protected Endpoint: PASS
- Token Validation: PASS
- Security: JWT verified
- Failures: 0
π₯ 15. REAL PRODUCTION PIPELINE
Code Push
β
Build Docker Image
β
Deploy (ECS / Kubernetes)
β
Run Newman (Auth + API tests)
β
IF FAIL β rollback
IF PASS β production
-
Top comments (0)