CRUD API dengan Node.js, Express, dan MySQL

CRUD API dengan Node.js, Express, dan MySQL

1. Struktur Project

crud-app/
├── src/
│   ├── config/
│   │   └── database.js
│   ├── controllers/
│   │   └── productController.js
│   ├── models/
│   │   └── productModel.js
│   ├── routes/
│   │   └── productRoutes.js
│   └── app.js
├── .env
└── package.json

2. Instalasi Dependencies

mkdir crud-app
cd crud-app
npm init -y

# Install dependencies
npm install express mysql2 dotenv cors
npm install --save-dev nodemon

3. Database Setup

CREATE DATABASE crud_db;
USE crud_db;

CREATE TABLE products (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(100) NOT NULL,
    price DECIMAL(10, 2) NOT NULL,
    stock INT NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

4. Konfigurasi Environment

# .env
PORT=3000
DB_HOST=127.0.0.1
DB_USER=root
DB_PASSWORD=yourpassword
DB_NAME=crud_db

5. Database Configuration

// src/config/database.js
const mysql = require('mysql2');
require('dotenv').config();

const pool = mysql.createPool({
    host: process.env.DB_HOST,
    user: process.env.DB_USER,
    password: process.env.DB_PASSWORD,
    database: process.env.DB_NAME
});

module.exports = pool.promise();

6. Product Model

// src/models/productModel.js
const db = require('../config/database');

class Product {
    static async findAll() {
        try {
            const [rows] = await db.query('SELECT * FROM products');
            return rows;
        } catch (error) {
            throw error;
        }
    }

    static async findById(id) {
        try {
            const [rows] = await db.query('SELECT * FROM products WHERE id = ?', [id]);
            return rows[0];
        } catch (error) {
            throw error;
        }
    }

    static async create(data) {
        try {
            const [result] = await db.query(
                'INSERT INTO products (name, price, stock) VALUES (?, ?, ?)',
                [data.name, data.price, data.stock]
            );
            return result.insertId;
        } catch (error) {
            throw error;
        }
    }

    static async update(id, data) {
        try {
            const [result] = await db.query(
                'UPDATE products SET name = ?, price = ?, stock = ? WHERE id = ?',
                [data.name, data.price, data.stock, id]
            );
            return result.affectedRows > 0;
        } catch (error) {
            throw error;
        }
    }

    static async delete(id) {
        try {
            const [result] = await db.query('DELETE FROM products WHERE id = ?', [id]);
            return result.affectedRows > 0;
        } catch (error) {
            throw error;
        }
    }
}

module.exports = Product;

7. Product Controller

// src/controllers/productController.js
const Product = require('../models/productModel');

class ProductController {
    // Get all products
    static async getProducts(req, res) {
        try {
            const products = await Product.findAll();
            res.json({
                status: 'success',
                data: products
            });
        } catch (error) {
            res.status(500).json({
                status: 'error',
                message: error.message
            });
        }
    }

    // Get single product
    static async getProduct(req, res) {
        try {
            const product = await Product.findById(req.params.id);
            if (!product) {
                return res.status(404).json({
                    status: 'error',
                    message: 'Product not found'
                });
            }
            res.json({
                status: 'success',
                data: product
            });
        } catch (error) {
            res.status(500).json({
                status: 'error',
                message: error.message
            });
        }
    }

    // Create product
    static async createProduct(req, res) {
        try {
            const { name, price, stock } = req.body;
            
            // Validasi input
            if (!name || !price || !stock) {
                return res.status(400).json({
                    status: 'error',
                    message: 'Please provide name, price and stock'
                });
            }

            const productId = await Product.create({ name, price, stock });
            res.status(201).json({
                status: 'success',
                message: 'Product created successfully',
                data: { id: productId }
            });
        } catch (error) {
            res.status(500).json({
                status: 'error',
                message: error.message
            });
        }
    }

    // Update product
    static async updateProduct(req, res) {
        try {
            const { name, price, stock } = req.body;
            const updated = await Product.update(req.params.id, { name, price, stock });
            
            if (!updated) {
                return res.status(404).json({
                    status: 'error',
                    message: 'Product not found'
                });
            }

            res.json({
                status: 'success',
                message: 'Product updated successfully'
            });
        } catch (error) {
            res.status(500).json({
                status: 'error',
                message: error.message
            });
        }
    }

    // Delete product
    static async deleteProduct(req, res) {
        try {
            const deleted = await Product.delete(req.params.id);
            
            if (!deleted) {
                return res.status(404).json({
                    status: 'error',
                    message: 'Product not found'
                });
            }

            res.json({
                status: 'success',
                message: 'Product deleted successfully'
            });
        } catch (error) {
            res.status(500).json({
                status: 'error',
                message: error.message
            });
        }
    }
}

module.exports = ProductController;

8. Product Routes

// src/routes/productRoutes.js
const express = require('express');
const router = express.Router();
const ProductController = require('../controllers/productController');

// Routes
router.get('/products', ProductController.getProducts);
router.get('/products/:id', ProductController.getProduct);
router.post('/products', ProductController.createProduct);
router.put('/products/:id', ProductController.updateProduct);
router.delete('/products/:id', ProductController.deleteProduct);

module.exports = router;

9. Express App Setup

// src/app.js
const express = require('express');
const cors = require('cors');
require('dotenv').config();

const productRoutes = require('./routes/productRoutes');

const app = express();

// Middleware
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// Routes
app.use('/api', productRoutes);

// Error handling
app.use((err, req, res, next) => {
    console.error(err.stack);
    res.status(500).json({
        status: 'error',
        message: 'Something broke!'
    });
});

// Start server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
    console.log(`Server is running on port ${PORT}`);
});

10. Package.json Scripts

tambahkan berikut di package.json

{
  "scripts": {
    "start": "node src/app.js",
    "dev": "nodemon src/app.js"
  }
}

hasil akhirnya


{
  "name": "crud_app",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node src/app.js",
    "dev": "nodemon src/app.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": "",
  "dependencies": {
    "cors": "^2.8.5",
    "dotenv": "^16.4.7",
    "express": "^4.21.1",
    "mysql2": "^3.11.5"
  },
  "devDependencies": {
    "nodemon": "^3.1.7"
  }
}

cara running nya

npm run dev

11. Testing API dengan Postman

GET All Products

GET http://localhost:3000/api/products

GET Single Product

GET http://localhost:3000/api/products/1

Create Product

POST http://localhost:3000/api/products
Content-Type: application/json

{
    "name": "Product 1",
    "price": 99.99,
    "stock": 100
}

Update Product

PUT http://localhost:3000/api/products/1
Content-Type: application/json

{
    "name": "Updated Product",
    "price": 149.99,
    "stock": 75
}

Delete Product

DELETE http://localhost:3000/api/products/1

Last updated