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)