Assalamualaikum Wr.Wb...
Berikut contoh lengkap REST API CRUD Product di Node.js dengan fitur:
- CRUD Product List data pagination
- Upload image & PDF
- Keamanan terhadap RCE, LFI, XSS, SQLi, file tampering / webshell
- Validasi & sanitasi input
- Proteksi upload file
Contoh ini memakai Express + Sequelize (MySQL).
1. Instalasi Dependency
npm init -ynpm install express sequelize sqlite3 multer helmet cors express-validatornpm install xss sanitize-filename file-typenpm install dotenv
2. Struktur Folder
project/
│── app.js
│── .env
│── database.js
│── package.json
│
├── models/
│ └── Product.js
│
├── controllers/
│ └── product.controller.js
│
├── routes/
│ └── product.routes.js
│
├── middlewares/
│ ├── upload.middleware.js
│ └── security.middleware.js
│
├── uploads/
│ ├── images/
│ └── files/
│
└── database/
└── product.sql
2. File .env
APP_PORT=3000DB_HOST=localhostDB_PORT=3306DB_NAME=product_dbDB_USER=rootDB_PASS=passwordkamuMAX_FILE_SIZE=2097152
3. Struktur Database MySQL
SQL Schema (database/product.sql)
CREATE DATABASE IF NOT EXISTS product_db;USE product_db;CREATE TABLE products (id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,name VARCHAR(255) NOT NULL,description TEXT,price DECIMAL(15,2) NOT NULL,image VARCHAR(255),document VARCHAR(255),created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP);CREATE INDEX idx_products_name ON products(name);
Aman dari serangan:
- SQL Injection → ORM
- Integer overflow → BIGINT
- File abuse → path tidak disimpan absolut
4. Konfigurasi Sequelize MySQL (database.js)
const { Sequelize } = require("sequelize");require("dotenv").config();const sequelize = new Sequelize(process.env.DB_NAME,process.env.DB_USER,process.env.DB_PASS,{host: process.env.DB_HOST,port: process.env.DB_PORT,dialect: "mysql",logging: false,dialectOptions: {multipleStatements: false},pool: {max: 10,min: 0,acquire: 30000,idle: 10000}});module.exports = sequelize;
Keamanan config database:
- multipleStatements: false → cegah SQLi chaining
- Connection pool stabil
5. Buat file Model Sequelize (models/Product.js)
const { DataTypes } = require("sequelize");const sequelize = require("../database");const Product = sequelize.define("Product",{id: {type: DataTypes.BIGINT.UNSIGNED,autoIncrement: true,primaryKey: true},name: {type: DataTypes.STRING(255),allowNull: false},description: {type: DataTypes.TEXT},price: {type: DataTypes.DECIMAL(15,2),allowNull: false},image: {type: DataTypes.STRING(255)},document: {type: DataTypes.STRING(255)}},{tableName: "products",underscored: true,timestamps: true});module.exports = Product;
6. Buat Middleware Keamanan Global di folder
(middlewares/security.middleware.js)
const helmet = require("helmet");const xss = require("xss");module.exports = (app) => {app.use(helmet());app.use((req, res, next) => {["body", "query", "params"].forEach((key) => {if (req[key]) {for (const prop in req[key]) {if (typeof req[key][prop] === "string") {req[key][prop] = xss(req[key][prop]);}}}});next();});};
Melindungi dari:
- XSS
- Header injection
- CSP bypass
7. Buat Upload File di (middlewares/upload.middleware.js)
const multer = require("multer");const path = require("path");const sanitize = require("sanitize-filename");const storage = multer.diskStorage({destination: (req, file, cb) => {if (file.mimetype.startsWith("image/")) {cb(null, "uploads/images");} else if (file.mimetype === "application/pdf") {cb(null, "uploads/files");} else {cb(new Error("Invalid file"));}},filename: (req, file, cb) => {const safeName = sanitize(file.originalname).replace(/\s+/g, "_").toLowerCase();cb(null, Date.now() + "-" + safeName);}});const fileFilter = (req, file, cb) => {const allowed = ["image/png","image/jpeg","application/pdf"];if (!allowed.includes(file.mimetype)) {return cb(new Error("File type forbidden"), false);}cb(null, true);};module.exports = multer({storage,fileFilter,limits: {fileSize: parseInt(process.env.MAX_FILE_SIZE)}});
Proteksi Upload:
- Tidak bisa upload ekstensi file contoh .js
- MIME whitelist
- Filename sanitized
- Size limit
- Tidak bisa path traversal
8. Buat Controller CRUD dan list Pagination
di controllers/product.controller.js
const Product = require("../models/Product");const { validationResult } = require("express-validator");exports.create = async (req, res) => {const errors = validationResult(req);if (!errors.isEmpty()) {return res.status(422).json(errors.array());}const product = await Product.create({name: req.body.name,description: req.body.description,price: req.body.price,image: req.files?.image?.[0]?.filename || null,document: req.files?.document?.[0]?.filename || null});res.json(product);};exports.list = async (req, res) => {const page = Math.max(parseInt(req.query.page) || 1, 1);const limit = Math.min(parseInt(req.query.limit) || 10, 50);const offset = (page - 1) * limit;const result = await Product.findAndCountAll({limit,offset,order: [["id", "DESC"]]});res.json({total: result.count,page,pages: Math.ceil(result.count / limit),data: result.rows});};exports.detail = async (req, res) => {const product = await Product.findByPk(req.params.id);if (!product) {return res.status(404).json({ message: "Not found" });}res.json(product);};exports.update = async (req, res) => {const product = await Product.findByPk(req.params.id);if (!product) {return res.status(404).json({ message: "Not found" });}await product.update(req.body);res.json(product);};exports.delete = async (req, res) => {const product = await Product.findByPk(req.params.id);if (!product) {return res.status(404).json({ message: "Not found" });}await product.destroy();res.json({ message: "Deleted" });};
9. Buat Routes di (routes/product.routes.js)
const express = require("express");const { body } = require("express-validator");const upload = require("../middlewares/upload.middleware");const controller = require("../controllers/product.controller");const router = express.Router();router.post("/",upload.fields([{ name: "image", maxCount: 1 },{ name: "document", maxCount: 1 }]),[body("name").isString().trim().notEmpty(),body("price").isFloat()],controller.create);router.get("/", controller.list);router.get("/:id", controller.detail);router.put("/:id", controller.update);router.delete("/:id", controller.delete);module.exports = router;
10. Buat App Utama file (app.js)
require("dotenv").config();const express = require("express");const cors = require("cors");const sequelize = require("./database");const security = require("./middlewares/security.middleware");const productRoutes = require("./routes/product.routes");const app = express();app.use(cors());app.use(express.json());app.use(express.urlencoded({ extended: false }));security(app);app.use("/uploads", express.static("uploads"));app.use("/api/products", productRoutes);// ini untuk Anti LFI & path traversalapp.use((req, res, next) => {if (req.url.includes("..")) {return res.status(403).json({ message: "Forbidden" });}next();});sequelize.authenticate().then(() => {console.log("DB Connected");sequelize.sync();});app.listen(process.env.APP_PORT, () =>console.log("Server running on port " + process.env.APP_PORT));
oke lanjut ke contoh hasil rest api dengan endpoint nya
BASE URL
http://localhost:3000/api/products
CREATE PRODUCT
POST /
Membuat produk baru dengan upload image & PDF
Endpoint
POST /api/products
di Headers
Content-Type multipart/form-data
Response Success (200)
{"id": 1,"name": "Produk A","description": "Deskripsi produk","price": "10000.00","image": "1700000000-image.jpg","document": "1700000000-manual.pdf","created_at": "2026-01-01T10:00:00.000Z","updated_at": "2026-01-01T10:00:00.000Z"}
Response Error 422 – Validation Error
[{"msg": "Invalid value","param": "price","location": "body"}]
400 – File Not Allowed
{
"message": "File type forbidden"
}
LIST PRODUCT (Pagination)
GET /
Endpoint
GET /api/products
Contoh Request
GET /api/products?page=1&limit=10
Response Success (200)
{"total": 25,"page": 1,"pages": 3,"data": [{"id": 5,"name": "Produk A","price": "10000.00","image": "1700000000-image.jpg","document": "1700000000-manual.pdf"}]}
DETAIL PRODUCT
GET /:id
Endpoint
GET /api/products/{id}
Contoh Request
GET /api/products/1
Response Success (200)
{"id": 1,"name": "Produk A","description": "Deskripsi produk","price": "10000.00","image": "1700000000-image.jpg","document": "1700000000-manual.pdf","created_at": "2026-01-01T10:00:00.000Z","updated_at": "2026-01-01T10:00:00.000Z"}
Response Error 404 – Not Found
{"message": "Not found"}
UPDATE PRODUCT
PUT /:id
Endpoint
PUT /api/products/{id}
Headers
Content-Type: application/json
Contoh Request
{"name": "Produk Update","price": 20000}
Response Success (200)
{"id": 1,"name": "Produk Update","price": "20000.00"}
DELETE PRODUCT
DELETE /:id
Endpoint
DELETE /api/products/{id}
Contoh Request
DELETE /api/products/1
Response Success (200)
{"message": "Deleted"}
Response Error
{"message": "Not found"}
oke mungkin itu saja sekian dan semoga bermanfaat....
Wasalamualaikum Wr.Wb...

0 Comments