Express
Framework Javascript NodeJS
Slide PPT : https://docs.google.com/presentation/d/1-NFflg1VVr75sQH_qDpaD59ksE5ENddJ
Pengenalan Express
- ExpressJS adalah salah satu Web Framework OpenSource paling populer di NodeJS
- ExpressJS pertama kali dibuat tahun 2010, dan karena sangat populer, ExpressJS sekarang sudah menjadi hal yang wajib dikuasai ketika kita akan membuat Web menggunakan NodeJS
- ExpressJS sangat minimalist, tidak memiliki banyak fitur seperti Web Framework di bahasa pemrograman lain seperti Laravel, Ruby on Rails atau Django
- Karena sangat minimalist, biasanya ExpressJS akan diintegrasikan dengan banyak library NodeJS lainnya
- Oleh karena itu, programmer bisa bebas memilih Library NodeJS yang ingin dia integrasikan dengan ExpressJS
Explore Express:
- Website Resmi Express : https://expressjs.com/
- Package : https://www.npmjs.com/package/express
- Github : https://github.com/expressjs/express
Install Express
npm install express
Menjalankan Aplication Express
import express from "express";
const app = express();
app.listen(3000, () => {
console.log("server running in port 3000");
});
Basic Routing
- Saat kita membuat web, biasanya kita akan membuat banyak sekali URL Path
- Routing merupakan teknik yang digunakan untuk meneruskan request dari URL Path ke callback yang kita tuju
- Routing di ExpressJS bisa menggunakan object Application, dan menggunakan method sesuai dengan nama HTTP Method nya
Routing Method
Method | Detail |
---|---|
app.connect(path, callback) | HTTP Method CONNECT |
app.get(path, callback) | HTTP Method GET |
app.post(path, callback) | HTTP Method POST |
app.put(path, callback) | HTTP Method PUT |
app.delete(path, callback) | HTTP Method DELETE |
app.options(path, callback) | HTTP Method OPTIONS |
app.trace(path, callback) | HTTP Method TRACE |
app.head(path, callback) | HTTP Method HEAD |
app.patch(path, callback) | HTTP Method PATCH |
app.all(path, callback) | Semua HTTP Method |
import express from "express";
const app = express();
app.get("/", (req, res) => {
res.send("hello world");
});
app.listen(3000, () => {
console.log("server running in port 3000");
});
Request
- Saat kita membuat callback di router, parameter pertama adalah object Request, yang secara otomatis diisi oleh ExpressJS
- Object Request akan berisikan informasi tentang HTTP Request yang masuk ke callback tersebut
- Ada banyak sekali informasi HTTP Request yang bisa kita ambil dari object Request, seperti Query Param, Header, Body dan lain-lain
Detail : https://expressjs.com/en/4x/api.html#req
Request URL
- Untuk mendapatkan URL saat ini, kita bisa menggunakan object Request untuk mendapatkan informasinya
req.originalUrl
, untuk mendapat url secara full beserta query param nyareq.path
, untuk mendapatkan path url tanpa query paramreq.hostname
, untuk mendapatkan nama host atau domain dari web kitareq.protocol
, untuk mendapatkan protocol dari url webreq.secure
, untuk mengecek apakah url web nya https atau bukanreq.subdomains
, untuk mendapatkan array subdomain dari url web kita
Request Query Params
- Request juga bisa digunakan untuk mengambil data query parameter
- Secara otomatis, semua query parameter akan disimpan dalam bentuk object di req.query
Detail : https://expressjs.com/en/4x/api.html#req.query
test("test request query", async () => {
app.get("/query", (req, res) => {
res.send(`hello ${req.query.firstName} ${req.query.lastName}`);
});
const response = await supertest(app).get("/query").query({ firstName: "joko", lastName: "santoso" });
expect(response.text).toBe("hello joko santoso");
});
Requets Header
- Object Request juga bisa kita gunakan untuk mendapatkan informasi dari HTTP Header dari Request
- Kita bisa menggunakan method req.get(name) atau req.header(name) untuk mendapatkan header berdasarkan name, khusus untuk HTTP Header, name nya adalah case insensitive
Detail : https://expressjs.com/en/4x/api.html#req.get
test("test request header", async () => {
app.get("/", (req, res) => {
const type = req.get("auth");
res.send(`auth is ${type}`);
});
const response = await supertest(app).get("/").set("auth", "123");
expect(response.text).toBe("auth is 123");
});
Request Body
- Sebelumnya kita belum membahas tentang HTTP Request Body
- Di ExpressJS, Secara default HTTP Request Body tidak bisa diambil datanya oleh Router Callback, hal ini dikarenakan, jenis data Request Body bisa berbeda-beda, tergantung tipe data yang dikirim
- Oleh karena itu, di dalam ExpressJS, terdapat Built-in Middleware, yang digunakan untuk membaca Request Body, lalu melakukan konversi ke tipe data yang diinginkan
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(expressFileUpload());
// request json
app.post("/json", (req, res) => {
const name = req.body.name;
res.json({ sayHello: `hello ${name}` });
});
// request form
app.post("/form", (req, res) => {
const name = req.body.name;
res.json({ sayHello: `hello ${name}` });
});
// request file
app.post("/file", async (req, res) => {
const textFile = req.files.article;
await textFile.mv(__dirname + "/upload/" + textFile.name);
res.send(`hello ${req.body.name}, file uploaded ${textFile.name}`);
});
test("test request body json", async () => {
const response = await supertest(app)
.post("/json")
.set("Content-Type", "application/json")
.send({ name: "joko" });
expect(response.body).toEqual({ sayHello: "hello joko" });
});
test("test request body form", async () => {
const response = await supertest(app)
.post("/form")
.set("Content-Type", "application/x-www-form-urlencoded")
.send("name=joko");
expect(response.body).toEqual({ sayHello: "hello joko" });
});
test("test request body file", async () => {
const response = await supertest(app)
.post("/file")
.set("Content-Type", "multipart/form-data")
.field("name", "joko")
.attach("article", __dirname + "/contoh.txt");
expect(response.text).toBe("hello joko, file uploaded contoh.txt");
});
Response
- Pada Callback Routing ExpressJS, terdapat parameter kedua yaitu response
- Response merupakan object representasi dari HTTP Response
- Kita bisa mengubah data HTTP Response melalui object Response tersebut
Detail : https://expressjs.com/en/4x/api.html#res
test("test response", async () => {
app.get("/", (req, res) => {
res.send("hello response");
});
const response = await supertest(app).get("/");
expect(response.text).toBe("hello response");
});
Response Status
Saat kita ingin mengubah HTTP Status dari HTTP Response yang kita buat, kita bisa menggunakan method res.status(code)
Detail : https://expressjs.com/en/4x/api.html#res.status
test("test response status", async () => {
app.get("/", (req, res) => {
if (req.query.name) {
res.status(200).send("status sucess");
} else {
res.status(400).end();
}
});
let response = await supertest(app).get("/").query({ name: "joko" });
expect(response.status).toBe(200);
expect(response.text).toBe("status sucess");
response = await supertest(app).get("/");
expect(response.status).toBe(400);
});
Response Header
- Kita juga bisa mengubah HTTP Response Header dengan menggunakan method res.set(name, value) atau res.header(name, value)
- Atau jika ingin langsung beberapa name, kita bisa masukkan dalam bentuk object ke dalam parameter name nya
Detail : https://expressjs.com/en/4x/api.html#res.set
test("test response header", async () => {
app.get("/", (req, res) => {
res
.set({
"X-Author": "Joko Santoso",
"X-Auth": "123",
})
.end();
});
const response = await supertest(app).get("/");
expect(response.get("x-author")).toBe("Joko Santoso");
expect(response.get("x-auth")).toBe("123");
});
Response Body
- Untuk mengubah Response Body, kita bisa menggunakan method
res.send(body)
- Dimana parameter body bisa kita kirim dalam bentuk
buffer
ataustring
, baik itu text, html, json dan lain-lain
Detail : https://expressjs.com/en/4x/api.html#res.send
test("test response", async () => {
app.get("/", (req, res) => {
res.set("Content-Type", "application/json");
res.json({ messages: "hello world" });
});
const response = await supertest(app).get("/");
expect(response.get("content-type")).toContain("application/json");
expect(response.body).toEqual({ messages: "hello world" });
});
Redirect
- Seperti yang pernah dijelaskan di kelas HTTP, untuk melakukan Redirect dari sebuah web ke halaman lain, kita hanya cukup menggunakan HTTP Header Location
- Di ExpressJS, kita bisa lakukan manual dengan menggunakan HTTP Header Location, atau bisa dengan bantuan method res.redirect(to)
Detail : https://expressjs.com/en/4x/api.html#res.redirect
test("test response", async () => {
app.get("/", (req, res) => {
res.redirect("/to-next-page");
});
const response = await supertest(app).get("/");
expect(response.get("Location")).toBe("/to-next-page");
});
Middleware
- Middleware adalah function yang bisa digunakan untuk mengakses request object, response object dan next function dalam alur hidup aplikasi ExpressJS
- Jika Middleware memanggil next function, artinya function Middleware selanjutnya atau Router akan dieksekusi
Fungsi Middleware
Ada banyak sekali kegunaan dari Middleware, seperti :
- Eksekusi kode sebelum sebelum router di eksekusi
- Mengubah Request atau Response object sebelum router di eksekusi
- Mengakhiri response tanpa harus mengeksekusi router
- Dan lain-lain
Spesifikasi Middleware
- Untuk membuat middleware, kita cukup membuat function dengan 3 parameter,
request
,response
dannext
- request adalah request object
- response adalah response object
- next adalah next function, bisa middleware selanjutnya atau router
export const apiKeyMiddleware = (req, res, next) => {
if (req.query.apikey) {
next();
} else {
res.status(401).end();
}
};
Next, di index.js
app.use(apiKeyMiddleware);
Manipulasi Request
- Karena Request itu adalah JavaScript Object
- Jadi jika kita mau, kita juga bisa memanipulasi Request Object di Middleware
- Misal mengubah attribute atau menambah attribute baru, agar bisa digunakan di Middleware selanjutnya, atau di Router
export const requestTimeMiddleware = (req, res, next) => {
const res.requestTime = Date.now();
next()
};
Next, di index.js
app.use(requestTimeMiddleware);
Type of Middleware
Di ExpressJS, terdapat beberapa jenis Middleware
- Application-level middleware
- Router-level middleware
- Error-handling middleware
- Built-in middleware
- Third-party middleware
Application-Level Middleware
- Yaitu middleware yang digunakan di object Application, sebelumnya kita sudah menggunakan Application-Level Middleware, dengan cara menggunakan function
app.use(middleware)
- Saat kita menggunakan Application-Level Middleware, maka secara otomatis Middleware tersebut akan dipanggil di semua route
- Jika kita mau menggunakan Middleware hanya untuk di route path tertentu, kita bisa tambahkan route pattern ketika menggunakan
app.use()
, misalapp.use(“/products/\*”, middleware)
Detail : https://expressjs.com/en/4x/api.html#app.use
const app = express();
app.use(middleware);
app.use("/product", middleware);
Router-Level Middleware
- Yaitu middleware yang ditambahkan pada object Router yang kita buat menggunakan
express.Router()
- Middleware ini secara otomatis akan dipanggil ketika request masuk ke router ini
- Sama seperti dengan Application-Level Middleware, jika kita ingin middleware nya hanya dipanggil para route path tertentu, kita bisa juga tambahkan route pattern ketika menggunakan middleware nya menggunakan
router.use(path, middleware)
Detail : https://expressjs.com/en/4x/api.html#router.use
const router = espress.Router();
router.use(middleware);
router.use("/product", middleware);
Error-Handling Middleware
- Yaitu middleware yang akan dipanggil ketika terjadi error di aplikasi kita (throw Error)
- Cara penggunaannya mirip dengan Application-Level Middleware, yang membedakan adalah function callback nya memiliki empat parameter, yaitu error, request, response dan next
- Object error akan secara otomatis terisi oleh data Error yang terjadi di aplikasi kita
- Middleware ini, sangat cocok ketika kita ingin menampilkan tampilan yang berbeda ketika terjadi error di aplikasi kita
const app = express();
app.use(errorMiddleware);
Built-in Middleware
- ExpressJS banyak sekali menggunakan Middleware untuk melakukan pemrosesan request dan response, termasuk terdapat Built-in Middleware, yaitu middleware yang sudah terdapat secara otomatis di ExpressJS
express.json()
, yaitu middleware yang melakukan parsing request body menjadi JavaScript objectexpress.text()
, yaitu middleware yang melakukan parsing request body menjadi stringexpress.raw()
, yaitu middleware yang melakukan parsing request body menjadi Bufferexpress.urlencoded()
, yaitu middleware yang melakukan parsing request body form menjadi objectexpress.static()
, yaitu middleware yang digunakan untuk melayani file static
Third-party middleware
- Yaitu middleware buatan orang lain yang kita gunakan
- Untuk menggunakannya, kita perlu menambah dependency middleware nya terlebih dahulu
Route Path
- Sebelumnya pada materi Basic Routing, kita belajar bagaimana cara melakukan routing dengan HTTP Method sesuai yang kita mau
- Sekarang kita akan bahas lebih detail tentang Route Path nya.
- Sebelumnya, route path yang kita gunakan tidak dinamis. ExpressJS mendukung route path yang dinamis, dengan cara menggunakan route path string patterns atau regex
// * -> all
app.get("/all/*", (req, res) => {
res.send(req.originalUrl);
});
// (\\d+) -> decimal
app.get("/decimal/*(\\d+)", (req, res) => {
res.send(req.originalUrl);
});
Route Parameter
- Saat kita membuat aplikasi Web API atau RESTful API, kadang kita sering menyimpan parameter dalam URL Path, misal
/products/{idProduct}
, atau/categories/{idCategory}
, dan lain-lain - ExpressJS mendukung penambahan parameter dalam route path, dengan menggunakan prefix :
titik dua
- Semua data parameter itu bisa kita tambahkan regex jika kita mau, misal
/products/:id(\\d+)
, artinya kita menambah parameter id, dimana id tersebut harus digit - Data route parameter secara otomatis bisa kita ambil sebagai attribute di
req.params
Detail : https://expressjs.com/en/4x/api.html#req.params
app.get("/product/:id", (req, res) => {
const id = req.params.id;
res.send(id);
});
Route Function
- Kadang ada kasus ketika kita membuat route path yang sama untuk beberapa tipe HTTP Method
- Pada kasus ini, kita bisa memanfaatkan route(path) function sehingga tidak perlu mendeklarasikan nama path sama untuk beberapa route
Detail : https://expressjs.com/en/4x/api.html#app.route
app
.route("/product")
.get((req, res) => {
res.send("get product");
})
.post((req, res) => {
res.send("create product");
})
.put((req, res) => {
res.send("update product");
});
Router
- Saat kita membuat Application ExpressJS, secara default sudah terdapat object Router nya
- Namun, kita bisa membuat object Router sendiri jika kita mau, hal ini sangat cocok jika kita ingin melakukan grouping Router, lalu misal kita bisa menambahkan Router tersebut ke Application seperti Middleware
- Ini sangat cocok ketika kita ingin membuat fitur modular yang bisa mengaktifkan atau menonaktifkan router secara dinamis misalnya
- Dengan object Router, kita bisa memiliki Middleware dan Routing secara independen
express router : https://expressjs.com/en/4x/api.html#express.router
router : https://expressjs.com/en/4x/api.html#router
const router = express.Router();
// midleware
router.use(middleware);
// path router
router.get("/", () => {});
Cookie
- Dalam HTTP, salah satu fitur yang biasa digunakan untuk pertukaran data dari Server dan Client adalah
Cookie
- Banyak yang menggunakan Cookie sebagai
Session
misalnya - Sayangnya, secara default, ExpressJS tidak mendukung Cookie, tapi jangan khawatir, kita bisa menggunakan Third-Party Middleware untuk mendukung Cookie ini yaitu
Cookie Parser
Cookie Parser
Cookie Parser adalah salah satu Third-Party Middleware yang bisa kita gunakan untuk mendukung fitur Cookie, dimana dengan Cookie Parser, kita secara otomatis menyimpan data ke Cookie, atau mengambil data ke Cookie
Detail : https://www.npmjs.com/package/cookie-parser
Istall Cookie Parser
npm install cookie-parser
Membaca Cookie
Setelah kita memasang Cookie Parser Middleware, kita bisa secara otomatis membaca Cookie yang dikirim dari Client melalui req.cookies
Detail : https://expressjs.com/en/4x/api.html#req.cookies
import cookieParser from "cookie-parser";
app.use(cookieParser());
app.get("/", (req, res) => {
const name = req.cookies["name"]; // or req.cookies.name
});
Menulis Cookie
Sedangkan untuk menulis Cookie, kita bisa tambahkan di response, dengan method res.cookie(key, value, setting)
Detail: https://expressjs.com/en/4x/api.html#res.cookie
app.post("/login", (req, res) => {
const name = req.body.name;
res.cookie("login", name, { path: "/" });
res.json({ sayHello: `hello ${name}` });
});
Dan untuk menghapus Cookie, kita bisa gunakan res.clearCookie(key, setting)
Detail : https://expressjs.com/en/4x/api.html#res.clearCookie
Signed Cookie
- Salah satu kelemahan ketika kita menyimpan data di Cookie adalah, Cookie bisa dimodifikasi oleh Client, misal kita bisa modifikasi Cookie di Browser kita
- Salah satu cara untuk menjaga agar Cookie tidak dimodifikasi adalah, kita menambahkan Signature pada Cookie kita
- Setiap nilai Cookie akan ada Signature, dimana ketika nilai Cookie diubah, otomatis Signature tidak akan sama lagi, dan secara otomatis value Cookie tidak dianggap valid lagi
- Fitur ini sudah ada di Cookie Parser dengan nama Signed Cookie
- Kita wajib menyebutkan Cookie mana yang ingin di Signed, ketika kita membuat Cookie di response
- Selain itu, kita juga perlu memasukkan Secret Key untuk digunakan ketika proses pembuatan Signature, pastikan Secret Key nya aman dan tidak mudah ditebak
app.use(cookieParser("RAHASIA"));
app.get("/", (req, res) => {
const name = req.signedCookies["login"];
res.json({ sayHello: `hello ${name}` });
});
app.post("/login", (req, res) => {
const name = req.body.name;
res.cookie("login", name, { path: "/", signed: true });
res.json({ sayHello: `hello ${name}` });
});
// Cookie -> "login=s%3Ajoko.5JIi4V1qI408khb6hyfNhvt46b9aBHFisyi6QcSrD8E; Path=/"
Response Body Lainya
Response Body Method | Keterangan |
---|---|
res.send(data) | Response berupa raw data |
res.download(path, filename, option) | Response berupa file download |
res.json(body) | Response berupa JSON |
res.redirect(url) | Response redirect url |
res.sendFile(path, option) | Response berupa file |
Error Handling
- Apa yang terjadi jika misal terjadi Error di aplikasi kita? Secara otomatis Error tersebut akan ditangkap oleh ExpressJS
- Lalu detail error nya akan ditampilkan di response-nya secara otomatis
- Kadang, ada kasus kita ingin mengubah cara menampilkan error, atau bahkan kita memang berharap terjadi error, misal Validation Error
- Pada kasus seperti ini, untungnya ExpressJS memiliki fitur
Error-Handling Middleware
, dimana kita bisa membuat Middleware dan akan dieksekusi ketika terjadi error - Berbeda dengan Middleware biasanya, pada Error-Handling Middleware, diperlukan empat parameter, dimana diawali dengan parameter error nya
const errorHandler = (err, req, res, next) => {
res.status(500).send("Terjadi error : " + err.message);
};
app.use(errorHandler); // tempatkan di paling bawah
Static File
- Saat membuat Web, kadang kita ingin menampilkan static file seperti html, css, javascript, gambar, atau file lainnya
- Jika kita harus membuat route untuk setiap file, maka akan menyulitkan.
- Untungnya, terdapat middleware yang bisa kita gunakan untuk menyediakan static file.
- Middleware ini secara otomatis akan mencari file, jika file ada, maka akan dikembalikan file tersebut, jika tidak ada, maka akan dilanjutkan ke middleware atau route selanjutnya
- Kita bisa menggunakan Middleware
express.static()
Detail : http://expressjs.com/en/4x/api.html#express.static
app.use(express.static(__dirname + "/static")); // /static/*
app.use("/static", express.static(__dirname + "/static")); // /static/static/*
Template Engine
- Saat membuat web menggunakan ExpressJS, maka jika kita membuat string HTML lalu kita kirim menggunakan response, maka hal itu sangat menyulitkan
- Biasanya, untuk mempermudah itu, kita bisa menggunakan Template Engine
- Template Engine adalah library yang digunakan untuk membuat template lalu mempermudah kita ketika ingin menampilkan data di template nya
- Biasanya template nya dalam bentuk HTML, dan data nya bisa kita ubah sesuai dengan data yang ingin kita tampilkan di HTML tersebut
Template Engine Library
ExpressJS Sendiri tidak memiliki fitur Template Engine, oleh karena itu kita perlu menggunakan library lain untuk menggunakan Template Engine Ada banyak sekali library Template Engine di NodeJS, misal :
- Mustache : https://github.com/janl/mustache.js/
- Pug : https://github.com/pugjs/pug
- EJS : https://github.com/mde/ejs
- Marko : https://github.com/marko-js/marko
- Dan lain-lain
Disini saya menggunakan Mustache
Hal ini dikarenakan Mustache merupakan template engine yang sangat mudah digunakan, kita tidak akan menginstall Mustache secara manual, kita akan menggunakan bantuan library Mustache Express
Detail : https://www.npmjs.com/package/mustache-express
Install Mustache
npm install mustache-express
Setup Mustche
import mustacheExpress from "mustache-express";
app.engine("html", mustacheExpress());
app.set("view engine", "html");
app.set("views", __dirname + "/views");
app.get("/", (req, res) => {
res.render("index", {
title: "mustache page",
say: "hello world",
});
});
Buat file index.html di views/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{{title}}</title>
</head>
<body>
<p>{{say}}</p>
</body>
</html>
File Upload
- Sebelumnya kita belum membahas bagaimana jika Request Body yang dikirim adalah File Upload atau Multipart Form Data?
- Sayangnya, secara default di ExpressJS, tidak ada fitur untuk membaca File Upload
- Tapi kita bisa menggunakan Third-Party Middleware lain untuk membaca File Upload
Install
npm install express-fileupload
Next
import expressFileUpload from "express-fileupload";
app.use(expressFileUpload());
app.post("/file", async (req, res) => {
const textFile = req.files.article;
await textFile.mv(__dirname + "/upload/" + textFile.name);
res.send(`hello ${req.body.name}, file uploaded ${textFile.name}`);
});
Not Found Error
- Saat user melakukan request ke URL yang tidak tersedia, maka secara default ExpressJS akan mengembalikan halaman 404
- Kadang ada kasus dimana kita ingin membuat halaman 404 sendiri
- Pada kasus ini, kita bisa menambahkan middleware di posisi paling akhir
- Middleware tersebut akan dipanggil jika memang tidak terdapat route yang tersedia untuk route path yang diakses
const notFoundMiddleware = (req, res, next) => {
res.status(404).send("404 Not Found Euy");
};
app.use(notFoundMiddleware);