Advanced Express
![Kamal [KL]](https://github.com/kamalMakarim.png)
Untuk meningkatkan backend Express.js, ada beberapa aspek penting yang perlu diperhatikan, seperti keamanan (security), skalabilitas (scalability), dan penanganan kesalahan (error handling). Berikut adalah penjelasan mengenai masing-masing aspek beserta contoh kode yang relevan.
1. Keamanan (Security)
Keamanan adalah aspek krusial dalam pengembangan backend. Beberapa teknik yang dapat diterapkan antara lain:
a. Hashing
Hashing digunakan untuk mengamankan data sensitif seperti kata sandi. Algoritma seperti SHA dan MD5 sudah tidak direkomendasikan karena rentan terhadap serangan. Sebagai gantinya, gunakan bcrypt
yang lebih aman.
Berikut adalah perbedaan utama antara SHA, MD5, dan bcrypt:
- SHA (Secure Hash Algorithm): Dirancang untuk menghasilkan hash unik dari data input. Contohnya adalah SHA-256 dan SHA-512. Namun, algoritma ini tidak cocok untuk hashing kata sandi karena hashingnya terlalu cepat, sehingga rentan terhadap serangan brute force.
- MD5 (Message Digest 5): Algoritma hashing yang lebih tua dan menghasilkan hash 128-bit. MD5 sudah dianggap tidak aman karena rentan terhadap serangan collision.
- bcrypt: Dirancang khusus untuk hashing kata sandi. bcrypt menggunakan salt untuk memastikan hash yang dihasilkan unik, mendukung work factor untuk meningkatkan keamanan, dan lebih lambat dibandingkan SHA dan MD5, sehingga lebih tahan terhadap brute force.
Contoh penggunaan bcrypt
:
const bcrypt = require("bcrypt");
const saltRounds = 10;
const plainPassword = "yourPassword";
// Hashing password
bcrypt.hash(plainPassword, saltRounds, function (err, hash) {
// Store hash in your password DB.
});
// Verifikasi password
bcrypt.compare(plainPassword, hash, (err, result) => {
if (err) throw err;
if (result) {
// Password cocok
} else {
// Password tidak cocok
}
});
b. CORS (Cross-Origin Resource Sharing)
CORS mengatur bagaimana sumber daya di server dapat diakses oleh domain lain. Untuk mengelola CORS di Express.js, Anda dapat menggunakan middleware cors
.
Contoh penerapan CORS:
const express = require("express");
const cors = require("cors");
const app = express();
const corsOptions = {
origin: "https://example.com",
methods: ["GET", "POST", "PUT", "DELETE"],
};
app.use(cors(corsOptions));
// Rute lainnya
c. Regex (Regular Expressions)
Regex (Regular Expressions) adalah pola teks yang digunakan untuk mencocokkan, mencari, atau memanipulasi string. Regex sangat berguna untuk validasi input, pencarian teks, atau penggantian teks dalam berbagai aplikasi.
Mari kita bedah regex yang digunakan dalam contoh:
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
Contoh Input dan Output:
- Valid:
example@example.com
user.name@domain.co
- Tidak Valid:
example@
(tidak ada domain)@example.com
(tidak ada nama pengguna)example@domain..com
(titik ganda tidak diizinkan)example@ domain.com
(whitespace tidak diizinkan)
Cara Kerja Fungsi:
function validateEmail(email) {
return emailRegex.test(email);
}
emailRegex.test(email)
: Mengembalikantrue
jika stringemail
cocok dengan pola regex, ataufalse
jika tidak.
Berikut adalah beberapa karakter kunci dalam regex:
Pattern | Penjelasan |
---|---|
[ ] | Untuk mendefinisikan range pencarian |
[^ ] | Untuk mencari karakter apa pun kecuali karakter di dalam tanda kurung siku |
[A-Z] | Untuk mencari huruf kapital (A sampai Z) |
[a-z] | Untuk mencari huruf non-kapital (a sampai z) |
| | Untuk mencari salah satu dari pola terkait (seperti OR) |
. | Untuk mencari hanya satu karakter apapun |
^ | Untuk mencari awalan String |
$ | Untuk mencari akhiran String |
\d | Untuk mencari digit angka |
\s | Untuk mencari whitespace |
\b | Untuk mencari awalan dan/atau akhiran kalimat |
\W | Untuk mencari karakter non-alphanumeric (special characters) |
\w | Untuk mencari karakter alphanumeric (huruf, angka, dan underscore) |
\uxxxx | Untuk mencari karakter unicode dalam hex |
n+ | Untuk mencari String apa saja minimal 1 |
n* | Untuk mencari String yang terdapat n (minimal 0 atau lebih) |
n? | Untuk mencari apakah ada n dalam String tersebut |
n{x} | Untuk mencari karakter sebanyak x |
n{x,y} | Untuk mencari karakter sebanyak range x sampai y |
Kelebihan Regex:
- Sangat fleksibel untuk mencocokkan pola teks yang kompleks.
- Dapat digunakan di berbagai bahasa pemrograman.
Kekurangan Regex:
- Sulit dibaca dan dipahami, terutama untuk pola yang kompleks.
- Tidak selalu cocok untuk semua jenis validasi (misalnya, validasi email yang lebih ketat memerlukan library khusus).
2. Skalabilitas (Scalability)
Untuk memastikan aplikasi dapat menangani peningkatan beban kerja, pertimbangkan hal-hal berikut:
a. Membuat dan melepas koneksi PostgreSQL untuk setiap query
Mengelola koneksi database dengan efisien sangat penting. Gunakan pool koneksi untuk mengurangi overhead pembuatan koneksi baru setiap kali melakukan query.
Contoh penggunaan pool koneksi dengan pg
:
const { Pool } = require("pg");
const pool = new Pool({
connectionString: PG_CONNECTION_STRING,
ssl: {
rejectUnauthorized: false,
},
});
// Membuat function query yang membuat lalu melepas koneksi
const query = async (text, params) => {
let client;
try {
client = await pool.connect();
const result = await client.query(text, params);
return result;
} catch (err) {
console.error("Database Query Error:", err);
throw err;
} finally {
if (client) client.release();
}
};
module.exports = {
pool,
query,
};
b. Efisiensi Query SQL
Contoh terdapat terdapat array yang berisi beberapa ID. terdapat beberapa metode untuk mengambil data pengguna berdasarkan ID tersebut:
- Menggunakan
.map
dan Promise
const ids = [
"550e8400-e29b-41d4-a716-446655440000",
"550e8400-e29b-41d4-a716-446655440001",
"550e8400-e29b-41d4-a716-446655440002",
];
const promises = ids.map((id) => {
return pool.query("SELECT * FROM users WHERE id = $1", [id]);
});
Promise.all(promises)
.then((results) => {
// Menggabungkan hasil
const users = results.map((r) => r.rows[0]);
})
.catch((err) => {
// Tangani kesalahan
});
Dengan menggunakan Promise.all
, Anda dapat menjalankan beberapa query secara paralel dan menggabungkan hasilnya.
- Menggunakan query dengan klausa
WHERE IN
const ids = [
"550e8400-e29b-41d4-a716-446655440000",
"550e8400-e29b-41d4-a716-446655440001",
"550e8400-e29b-41d4-a716-446655440002",
];
const query = "SELECT * FROM users WHERE id = ANY($1::uuid[])";
pool.query(query, [ids], (err, res) => {
if (err) throw err;
const users = res.rows;
// Proses data pengguna
});
Metode kedua lebih efisien karena hanya melakukan satu query ke database. Namun, metode pertama lebih fleksibel jika Anda perlu melakukan operasi tambahan pada setiap hasil query.
3. Penanganan Kesalahan (Error Handling)
Penanganan kesalahan yang baik meningkatkan keandalan aplikasi.
a. Memastikan body atau header yang diperlukan diberikan
Contoh pengecekan parameter body :
exports.login = async (req, res) => {
// Memeriksa keberadaan body
if (!req.query.email || !req.query.password) {
return res.status(400).send("Missing email or password");
}
try {
// Proses login
} catch (error) {
// Tangani kesalahan
}
};
b. Menggunakan BEGIN
, COMMIT
, atau ROLLBACK
untuk query yang mengubah data
Dalam operasi yang melibatkan beberapa query yang saling bergantung, gunakan transaksi untuk memastikan konsistensi data.
Contoh penggunaan transaksi dengan pg
:
require("dotenv").config();
const { Pool } = require("pg");
const { PG_CONNECTION_STRING } = process.env;
const pool = new Pool({
connectionString: PG_CONNECTION_STRING,
ssl: {
rejectUnauthorized: false,
},
});
const transaction = async (callback) => {
let client;
try {
client = await pool.connect();
await client.query("BEGIN");
const result = await callback(client);
await client.query("COMMIT");
return result;
} catch (err) {
await client.query("ROLLBACK");
console.error("Transaction Error:", err);
throw err;
} finally {
if (client) client.release();
}
};
module.exports = {
pool,
transaction,
};
Dengan menerapkan praktik-praktik di atas, Anda dapat meningkatkan keamanan, skalabilitas, dan keandalan backend Express.js Anda.