לדלג לתוכן

בניית API באמצעות Express

ספריית Express היא חבילת Node.js לבניית שרתי Web ו-API.

במקום לעבוד ישירות עם http module (שזה מסובך), Express נותן:

  • ניהול Routes
  • עבודה עם JSON
  • Middleware
  • ניהול בקשות HTTP
  • קל מאוד לבנות REST API

ממש כמו fastapi אבל בjs

התקנה

npm init -y
npm install express mongoose jsonwebtoken bcrypt dotenv

מבנה פרויקט

project/
├── src/
│   ├── server.js
│   │
│   ├── models/
│   │   └── user.model.js
│   │
│   ├── controllers/
│   │   ├── user.controller.js
│   │   └── auth.controller.js
│   │
│   ├── routes/
│   │   ├── user.routes.js
│   │   └── auth.routes.js
│   │
│   └── middlewares/
│       ├── auth.middleware.js
│       └── error.middleware.js
└── .env

קובץ הmain

src/server.js

require("dotenv").config()
const express = require("express")
const mongoose = require("mongoose")

const userRoutes = require("./routes/user.routes")
const authRoutes = require("./routes/auth.routes")
const errorMiddleware = require("./middlewares/error.middleware")

const app = express()

app.use(express.json())

app.use("/api/users", userRoutes)
app.use("/api/auth", authRoutes)

app.use(errorMiddleware)

mongoose.connect(process.env.MONGO_URL)
  .then(() => {
    console.log("DB Connected")
    app.listen(3000, () => console.log("Server running"))
  })

כאן אנחנו מגדירים את כל הrouter-ים, מתחברים לmongo db, טוענים את הקובץ .env שמכיל את הuri של הmongo במקרה שלנו.

Model- MongoDB

models/user.model.js

const mongoose = require("mongoose")

const schema = new mongoose.Schema({
  email: { type: String, unique: true },
  password: String,
  name: String,
  createdAt: { type: Date, default: Date.now }
})

module.exports = mongoose.model("User", schema)

Controller

controllers/user.controller.js

const User = require("../models/user.model")

exports.getAll = async (req, res, next) => {
  try {
    const users = await User.find()
    res.json(users)
  } catch (err) {
    next(err)
  }
}

exports.getOne = async (req, res, next) => {
  try {
    const user = await User.findById(req.params.id)
    if (!user) return res.sendStatus(404)
    res.json(user)
  } catch (err) {
    next(err)
  }
}

exports.create = async (req, res, next) => {
  try {
    const user = await User.create(req.body)
    res.status(201).json(user)
  } catch (err) {
    next(err)
  }
}

exports.update = async (req, res, next) => {
  try {
    const user = await User.findByIdAndUpdate(
      req.params.id,
      req.body,
      { new: true }
    )
    res.json(user)
  } catch (err) {
    next(err)
  }
}

exports.remove = async (req, res, next) => {
  try {
    await User.findByIdAndDelete(req.params.id)
    res.sendStatus(204)
  } catch (err) {
    next(err)
  }
}

Authentication Controller

controllers/auth.controller.js

const User = require("../models/user.model")
const bcrypt = require("bcrypt")
const jwt = require("jsonwebtoken")

exports.register = async (req, res, next) => {
  try {
    const hash = await bcrypt.hash(req.body.password, 10)

    const user = await User.create({
      email: req.body.email,
      password: hash,
      name: req.body.name
    })

    res.status(201).json(user)
  } catch (err) {
    next(err)
  }
}

exports.login = async (req, res, next) => {
  try {
    const user = await User.findOne({ email: req.body.email })
    if (!user) return res.status(401).json({ error: "Invalid" })

    const ok = await bcrypt.compare(req.body.password, user.password)
    if (!ok) return res.status(401).json({ error: "Invalid" })

    const token = jwt.sign(
      { id: user._id },
      process.env.JWT_SECRET
    )

    res.json({ token })
  } catch (err) {
    next(err)
  }
}

Routes

routes/user.routes.js

const router = require("express").Router()
const ctrl = require("../controllers/user.controller")
const auth = require("../middlewares/auth.middleware")

router.get("/", auth, ctrl.getAll)
router.get("/:id", auth, ctrl.getOne)
router.post("/", ctrl.create)
router.put("/:id", auth, ctrl.update)
router.delete("/:id", auth, ctrl.remove)

module.exports = router

routes/auth.routes.js

const router = require("express").Router()
const ctrl = require("../controllers/auth.controller")

router.post("/register", ctrl.register)
router.post("/login", ctrl.login)

module.exports = router

Middleware- JWT

middlewares/auth.middleware.js

בexpress אנחנו יכולים להגדיר middleware.
קטע קוד שרץ בכל בקשה שקורת לapi שלנו, שמבצעת לוגיקה מסוימת.
זה יכול להיות קטע קוד שעושה לוג לכל הבקשות, ועוד.
במקרה הבא זה קטע קוד שעושה authorization (בודק אם העובר jwt token תקין)

const jwt = require("jsonwebtoken")

module.exports = (req, res, next) => {
  const token = req.headers.authorization?.split(" ")[1]
  if (!token) return res.sendStatus(401)

  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET)
    req.user = decoded
    next()
  } catch {
    res.sendStatus(403)
  }
}


Error Middleware מרכזי

middlewares/error.middleware.js

module.exports = (err, req, res, next) => {
  console.error(err)
  res.status(500).json({ error: "Server error" })
}

איך Express עובד- הסבר פשוט

כשלקוח שולח בקשה:

GET /api/users

הזרימה:

  1. הבקשה נכנסת ל-Express
  2. הExpress עובר על middleware (למשל JSON parser)
  3. הExpress מוצא route מתאים
  4. אם יש auth middleware → רץ
  5. ה-Controller מופעל
  6. הController מדבר עם DB
  7. מחזירים JSON

לסיכום:
חבילת express מאפשרת לנו לכתוב api בפשטות בjs, ונהוג להשתמש בה או בספריות דומות כאשר בונים api בjs.