Tools: Building a Scalable Auth Service Using Node.js, Express, PostgreSQL, and Prisma (Microservices Architecture)

Tools: Building a Scalable Auth Service Using Node.js, Express, PostgreSQL, and Prisma (Microservices Architecture)

Below is a production-style Auth Service for your Local Marketplace Microservices Platform using: This structure is similar to what engineers use in production systems at companies like Stripe and Shopify.
πŸ“ Auth Service Folder Structure
auth-service
β”‚
β”œβ”€β”€ src
β”‚ β”œβ”€β”€ config
β”‚ β”‚ └── db.js
β”‚ β”‚
β”‚ β”œβ”€β”€ controllers
β”‚ β”‚ └── auth.controller.js
β”‚ β”‚
β”‚ β”œβ”€β”€ services
β”‚ β”‚ └── auth.service.js
β”‚ β”‚
β”‚ β”œβ”€β”€ repositories
β”‚ β”‚ └── user.repository.js
β”‚ β”‚
β”‚ β”œβ”€β”€ routes
β”‚ β”‚ └── auth.routes.js
β”‚ β”‚
β”‚ β”œβ”€β”€ middlewares
β”‚ β”‚ └── auth.middleware.js
β”‚ β”‚
β”‚ β”œβ”€β”€ utils
β”‚ β”‚ β”œβ”€β”€ hash.js
β”‚ β”‚ └── jwt.js
β”‚ β”‚
β”‚ β”œβ”€β”€ app.js
β”‚ └── server.js
β”‚
β”œβ”€β”€ prisma
β”‚ β”œβ”€β”€ schema.prisma
β”‚ └── seed.js
β”‚
β”œβ”€β”€ .env
β”œβ”€β”€ package.json
└── Dockerfile
1️⃣ package.json 3️⃣ Prisma Schema
πŸ“ prisma/schema.prisma πŸ“ src/repositories/user.repository.js πŸ“ src/services/auth.service.js πŸ“ src/controllers/auth.controller.js πŸ“ src/routes/auth.routes.js πŸš€ API Endpoints
Register User ⭐ What Makes This Production-Ready βœ” Layered architecture
βœ” Repository pattern
βœ” Prisma ORM
βœ” JWT authentication
βœ” Microservice-ready
βœ” Docker-ready
βœ” Scalable structure Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to ? It will become hidden in your post, but will still be visible via the comment's permalink. as well , this person and/or CODE_BLOCK:
{ "name": "auth-service", "version": "1.0.0", "main": "src/server.js", "scripts": { "dev": "nodemon src/server.js", "start": "node src/server.js", "prisma": "prisma migrate dev" }, "dependencies": { "@prisma/client": "^5.0.0", "bcryptjs": "^2.4.3", "dotenv": "^16.0.0", "express": "^4.18.2", "jsonwebtoken": "^9.0.0" }, "devDependencies": { "nodemon": "^3.0.0", "prisma": "^5.0.0" }
} CODE_BLOCK:
{ "name": "auth-service", "version": "1.0.0", "main": "src/server.js", "scripts": { "dev": "nodemon src/server.js", "start": "node src/server.js", "prisma": "prisma migrate dev" }, "dependencies": { "@prisma/client": "^5.0.0", "bcryptjs": "^2.4.3", "dotenv": "^16.0.0", "express": "^4.18.2", "jsonwebtoken": "^9.0.0" }, "devDependencies": { "nodemon": "^3.0.0", "prisma": "^5.0.0" }
} CODE_BLOCK:
{ "name": "auth-service", "version": "1.0.0", "main": "src/server.js", "scripts": { "dev": "nodemon src/server.js", "start": "node src/server.js", "prisma": "prisma migrate dev" }, "dependencies": { "@prisma/client": "^5.0.0", "bcryptjs": "^2.4.3", "dotenv": "^16.0.0", "express": "^4.18.2", "jsonwebtoken": "^9.0.0" }, "devDependencies": { "nodemon": "^3.0.0", "prisma": "^5.0.0" }
} CODE_BLOCK:
PORT=4000 DATABASE_URL="postgresql://postgres:password@localhost:5432/auth_db" JWT_SECRET=supersecret CODE_BLOCK:
PORT=4000 DATABASE_URL="postgresql://postgres:password@localhost:5432/auth_db" JWT_SECRET=supersecret CODE_BLOCK:
PORT=4000 DATABASE_URL="postgresql://postgres:password@localhost:5432/auth_db" JWT_SECRET=supersecret CODE_BLOCK:
generator client { provider = "prisma-client-js"
} datasource db { provider = "postgresql" url = env("DATABASE_URL")
} model User { id String @id @default(uuid()) name String email String @unique password String role String @default("CUSTOMER") createdAt DateTime @default(now())
} CODE_BLOCK:
generator client { provider = "prisma-client-js"
} datasource db { provider = "postgresql" url = env("DATABASE_URL")
} model User { id String @id @default(uuid()) name String email String @unique password String role String @default("CUSTOMER") createdAt DateTime @default(now())
} CODE_BLOCK:
generator client { provider = "prisma-client-js"
} datasource db { provider = "postgresql" url = env("DATABASE_URL")
} model User { id String @id @default(uuid()) name String email String @unique password String role String @default("CUSTOMER") createdAt DateTime @default(now())
} CODE_BLOCK:
npx prisma generate
npx prisma migrate dev --name init CODE_BLOCK:
npx prisma generate
npx prisma migrate dev --name init CODE_BLOCK:
npx prisma generate
npx prisma migrate dev --name init CODE_BLOCK:
const { PrismaClient } = require('@prisma/client') const prisma = new PrismaClient() module.exports = prisma CODE_BLOCK:
const { PrismaClient } = require('@prisma/client') const prisma = new PrismaClient() module.exports = prisma CODE_BLOCK:
const { PrismaClient } = require('@prisma/client') const prisma = new PrismaClient() module.exports = prisma COMMAND_BLOCK:
const bcrypt = require('bcryptjs') exports.hashPassword = async (password) => { return bcrypt.hash(password, 10)
} exports.comparePassword = async (password, hash) => { return bcrypt.compare(password, hash)
} COMMAND_BLOCK:
const bcrypt = require('bcryptjs') exports.hashPassword = async (password) => { return bcrypt.hash(password, 10)
} exports.comparePassword = async (password, hash) => { return bcrypt.compare(password, hash)
} COMMAND_BLOCK:
const bcrypt = require('bcryptjs') exports.hashPassword = async (password) => { return bcrypt.hash(password, 10)
} exports.comparePassword = async (password, hash) => { return bcrypt.compare(password, hash)
} COMMAND_BLOCK:
const jwt = require('jsonwebtoken') exports.generateToken = (payload) => { return jwt.sign(payload, process.env.JWT_SECRET, { expiresIn: "1d" })
} COMMAND_BLOCK:
const jwt = require('jsonwebtoken') exports.generateToken = (payload) => { return jwt.sign(payload, process.env.JWT_SECRET, { expiresIn: "1d" })
} COMMAND_BLOCK:
const jwt = require('jsonwebtoken') exports.generateToken = (payload) => { return jwt.sign(payload, process.env.JWT_SECRET, { expiresIn: "1d" })
} COMMAND_BLOCK:
const prisma = require('../config/db') exports.createUser = (data) => { return prisma.user.create({ data })
} exports.findUserByEmail = (email) => { return prisma.user.findUnique({ where: { email } })
} COMMAND_BLOCK:
const prisma = require('../config/db') exports.createUser = (data) => { return prisma.user.create({ data })
} exports.findUserByEmail = (email) => { return prisma.user.findUnique({ where: { email } })
} COMMAND_BLOCK:
const prisma = require('../config/db') exports.createUser = (data) => { return prisma.user.create({ data })
} exports.findUserByEmail = (email) => { return prisma.user.findUnique({ where: { email } })
} COMMAND_BLOCK:
const userRepo = require('../repositories/user.repository')
const { hashPassword, comparePassword } = require('../utils/hash')
const { generateToken } = require('../utils/jwt') exports.register = async (data) => { const existingUser = await userRepo.findUserByEmail(data.email) if (existingUser) { throw new Error("User already exists") } const hashedPassword = await hashPassword(data.password) const user = await userRepo.createUser({ ...data, password: hashedPassword }) return user
} exports.login = async ({ email, password }) => { const user = await userRepo.findUserByEmail(email) if (!user) { throw new Error("Invalid credentials") } const valid = await comparePassword(password, user.password) if (!valid) { throw new Error("Invalid credentials") } const token = generateToken({ id: user.id, email: user.email }) return { token }
} COMMAND_BLOCK:
const userRepo = require('../repositories/user.repository')
const { hashPassword, comparePassword } = require('../utils/hash')
const { generateToken } = require('../utils/jwt') exports.register = async (data) => { const existingUser = await userRepo.findUserByEmail(data.email) if (existingUser) { throw new Error("User already exists") } const hashedPassword = await hashPassword(data.password) const user = await userRepo.createUser({ ...data, password: hashedPassword }) return user
} exports.login = async ({ email, password }) => { const user = await userRepo.findUserByEmail(email) if (!user) { throw new Error("Invalid credentials") } const valid = await comparePassword(password, user.password) if (!valid) { throw new Error("Invalid credentials") } const token = generateToken({ id: user.id, email: user.email }) return { token }
} COMMAND_BLOCK:
const userRepo = require('../repositories/user.repository')
const { hashPassword, comparePassword } = require('../utils/hash')
const { generateToken } = require('../utils/jwt') exports.register = async (data) => { const existingUser = await userRepo.findUserByEmail(data.email) if (existingUser) { throw new Error("User already exists") } const hashedPassword = await hashPassword(data.password) const user = await userRepo.createUser({ ...data, password: hashedPassword }) return user
} exports.login = async ({ email, password }) => { const user = await userRepo.findUserByEmail(email) if (!user) { throw new Error("Invalid credentials") } const valid = await comparePassword(password, user.password) if (!valid) { throw new Error("Invalid credentials") } const token = generateToken({ id: user.id, email: user.email }) return { token }
} COMMAND_BLOCK:
const authService = require('../services/auth.service') exports.register = async (req, res) => { try { const user = await authService.register(req.body) res.status(201).json(user) } catch (error) { res.status(400).json({ message: error.message }) }
} exports.login = async (req, res) => { try { const result = await authService.login(req.body) res.json(result) } catch (error) { res.status(401).json({ message: error.message }) }
} COMMAND_BLOCK:
const authService = require('../services/auth.service') exports.register = async (req, res) => { try { const user = await authService.register(req.body) res.status(201).json(user) } catch (error) { res.status(400).json({ message: error.message }) }
} exports.login = async (req, res) => { try { const result = await authService.login(req.body) res.json(result) } catch (error) { res.status(401).json({ message: error.message }) }
} COMMAND_BLOCK:
const authService = require('../services/auth.service') exports.register = async (req, res) => { try { const user = await authService.register(req.body) res.status(201).json(user) } catch (error) { res.status(400).json({ message: error.message }) }
} exports.login = async (req, res) => { try { const result = await authService.login(req.body) res.json(result) } catch (error) { res.status(401).json({ message: error.message }) }
} CODE_BLOCK:
const express = require('express')
const router = express.Router() const authController = require('../controllers/auth.controller') router.post('/register', authController.register) router.post('/login', authController.login) module.exports = router CODE_BLOCK:
const express = require('express')
const router = express.Router() const authController = require('../controllers/auth.controller') router.post('/register', authController.register) router.post('/login', authController.login) module.exports = router CODE_BLOCK:
const express = require('express')
const router = express.Router() const authController = require('../controllers/auth.controller') router.post('/register', authController.register) router.post('/login', authController.login) module.exports = router CODE_BLOCK:
const express = require('express')
const authRoutes = require('./routes/auth.routes') const app = express() app.use(express.json()) app.use('/auth', authRoutes) module.exports = app CODE_BLOCK:
const express = require('express')
const authRoutes = require('./routes/auth.routes') const app = express() app.use(express.json()) app.use('/auth', authRoutes) module.exports = app CODE_BLOCK:
const express = require('express')
const authRoutes = require('./routes/auth.routes') const app = express() app.use(express.json()) app.use('/auth', authRoutes) module.exports = app COMMAND_BLOCK:
require('dotenv').config() const app = require('./app') const PORT = process.env.PORT || 4000 app.listen(PORT, () => { console.log(`Auth service running on port ${PORT}`)
}) COMMAND_BLOCK:
require('dotenv').config() const app = require('./app') const PORT = process.env.PORT || 4000 app.listen(PORT, () => { console.log(`Auth service running on port ${PORT}`)
}) COMMAND_BLOCK:
require('dotenv').config() const app = require('./app') const PORT = process.env.PORT || 4000 app.listen(PORT, () => { console.log(`Auth service running on port ${PORT}`)
}) CODE_BLOCK:
FROM node:20 WORKDIR /app COPY package*.json ./ RUN npm install COPY . . EXPOSE 4000 CMD ["npm","start"] CODE_BLOCK:
FROM node:20 WORKDIR /app COPY package*.json ./ RUN npm install COPY . . EXPOSE 4000 CMD ["npm","start"] CODE_BLOCK:
FROM node:20 WORKDIR /app COPY package*.json ./ RUN npm install COPY . . EXPOSE 4000 CMD ["npm","start"] CODE_BLOCK:
POST /auth/register CODE_BLOCK:
POST /auth/register CODE_BLOCK:
POST /auth/register CODE_BLOCK:
{ "name": "John", "email": "[email protected]", "password": "123456"
} CODE_BLOCK:
{ "name": "John", "email": "[email protected]", "password": "123456"
} CODE_BLOCK:
{ "name": "John", "email": "[email protected]", "password": "123456"
} CODE_BLOCK:
POST /auth/login CODE_BLOCK:
POST /auth/login CODE_BLOCK:
POST /auth/login CODE_BLOCK:
{ "email": "[email protected]", "password": "123456"
} CODE_BLOCK:
{ "email": "[email protected]", "password": "123456"
} CODE_BLOCK:
{ "email": "[email protected]", "password": "123456"
} - JWT Authentication
- Docker-ready structure
- Layered architecture (Controller β†’ Service β†’ Repository)