Membuat REST API Aman di Node.js

Diterbitkan: 2022-03-11

Antarmuka pemrograman aplikasi (API) ada di mana-mana. Mereka memungkinkan perangkat lunak untuk berkomunikasi dengan perangkat lunak lain—internal atau eksternal—secara konsisten, yang merupakan unsur utama dalam skalabilitas, belum lagi dapat digunakan kembali.

Saat ini cukup umum bagi layanan online untuk memiliki API yang menghadap publik. Ini memungkinkan pengembang lain untuk dengan mudah mengintegrasikan fitur-fitur seperti login media sosial, pembayaran kartu kredit, dan pelacakan perilaku. Standar de facto yang mereka gunakan untuk ini disebut REpresentational State Transfer (REST).

Meskipun banyak platform dan bahasa pemrograman dapat digunakan untuk tugas tersebut—misalnya, ASP.NET Core, Laravel (PHP), atau Bottle (Python)—dalam tutorial ini, kita akan membangun REST API dasar namun aman dengan menggunakan tumpukan berikut:

  • Node.js, yang seharusnya sudah familiar bagi pembaca
  • Express, yang sangat menyederhanakan membangun tugas server web umum di bawah Node.js dan merupakan tarif standar dalam membangun back end REST API
  • Mongoose, yang akan menghubungkan back end kita ke database MongoDB

Pengembang yang mengikuti tutorial ini juga harus merasa nyaman dengan terminal (atau command prompt).

Catatan: Kami tidak akan membahas basis kode front-end di sini, tetapi fakta bahwa backend kami ditulis dalam JavaScript membuatnya nyaman untuk berbagi kode—model objek, misalnya—di seluruh tumpukan penuh.

Anatomi REST API

REST API digunakan untuk mengakses dan memanipulasi data menggunakan serangkaian operasi stateless yang umum. Operasi ini merupakan bagian integral dari protokol HTTP dan mewakili fungsionalitas buat, baca, perbarui, dan hapus (CRUD) yang penting, meskipun tidak dengan cara satu-ke-satu yang bersih:

  • POST (membuat sumber daya atau umumnya menyediakan data)
  • GET (mengambil indeks sumber daya atau sumber daya individu)
  • PUT (membuat atau mengganti sumber daya)
  • PATCH (memperbarui/memodifikasi sumber daya)
  • DELETE (menghapus sumber daya)

Menggunakan operasi HTTP ini dan nama sumber daya sebagai alamat, kita dapat membangun REST API dengan membuat titik akhir untuk setiap operasi. Dan dengan menerapkan polanya, kita akan memiliki fondasi yang stabil dan mudah dipahami yang memungkinkan kita untuk mengembangkan kode dengan cepat dan memeliharanya setelahnya. Seperti disebutkan sebelumnya, fondasi yang sama akan digunakan untuk mengintegrasikan fitur pihak ketiga, yang sebagian besar juga menggunakan REST API, membuat integrasi tersebut lebih cepat.

Untuk saat ini, mari mulai membuat REST API aman menggunakan Node.js!

Dalam tutorial ini, kita akan membuat REST API yang cukup umum (dan sangat praktis) untuk sumber daya yang disebut users .

Sumber daya kami akan memiliki struktur dasar berikut:

  • id (UUID yang dibuat secara otomatis)
  • firstName
  • lastName
  • email
  • password
  • permissionLevel (apa yang boleh dilakukan pengguna ini?)

Dan kami akan membuat operasi berikut untuk sumber daya itu:

  • POST di endpoint /users (buat pengguna baru)
  • GET di titik akhir /users (daftar semua pengguna)
  • GET di titik akhir /users/:userId (dapatkan pengguna tertentu)
  • PATCH pada titik akhir /users/:userId (perbarui data untuk pengguna tertentu)
  • DELETE pada titik akhir /users/:userId (hapus pengguna tertentu)

Kami juga akan menggunakan token web JSON (JWT) untuk token akses. Untuk itu, kami akan membuat sumber daya lain yang disebut auth yang akan mengharapkan email dan kata sandi pengguna dan, sebagai gantinya, akan menghasilkan token yang digunakan untuk otentikasi pada operasi tertentu. (Artikel hebat Dejan Milosevic tentang JWT untuk aplikasi REST yang aman di Java menjelaskan lebih lanjut tentang ini; prinsipnya sama.)

Pengaturan Tutorial REST API

Pertama-tama, pastikan Anda menginstal Node.js versi terbaru. Untuk artikel ini, saya akan menggunakan versi 14.9.0; itu juga dapat bekerja pada versi yang lebih lama.

Selanjutnya, pastikan Anda telah menginstal MongoDB. Kami tidak akan menjelaskan secara spesifik Mongoose dan MongoDB yang digunakan di sini, tetapi untuk menjalankan dasar-dasarnya, cukup jalankan server dalam mode interaktif (yaitu, dari baris perintah sebagai mongo ) daripada sebagai layanan. Itu karena, pada satu titik dalam tutorial ini, kita harus berinteraksi dengan MongoDB secara langsung daripada melalui kode Node.js kita.

Catatan: Dengan MongoDB, tidak perlu membuat database tertentu seperti yang mungkin ada di beberapa skenario RDBMS. Panggilan insert pertama dari kode Node.js kami akan memicu pembuatannya secara otomatis.

Tutorial ini tidak berisi semua kode yang diperlukan untuk proyek kerja. Ini dimaksudkan sebagai gantinya Anda mengkloning repo pendamping dan cukup mengikuti sorotan saat Anda membaca — tetapi Anda juga dapat menyalin file dan cuplikan tertentu dari repo sesuai kebutuhan, jika Anda mau.

Arahkan ke folder rest-api-tutorial/ yang dihasilkan di terminal Anda. Anda akan melihat bahwa proyek kami berisi tiga folder modul:

  • common (menangani semua layanan bersama, dan berbagi informasi antar modul pengguna)
  • users (segala sesuatu tentang pengguna)
  • auth (menangani pembuatan JWT dan alur masuk)

Sekarang, jalankan npm install (atau yarn jika Anda memilikinya.)

Selamat, Anda sekarang memiliki semua dependensi dan penyiapan yang diperlukan untuk menjalankan back end REST API sederhana kami.

Membuat Modul Pengguna

Kami akan menggunakan Mongoose, perpustakaan pemodelan data objek (ODM) untuk MongoDB, untuk membuat model pengguna dalam skema pengguna.

Pertama, kita perlu membuat skema Mongoose di /users/models/users.model.js :

 const userSchema = new Schema({ firstName: String, lastName: String, email: String, password: String, permissionLevel: Number });

Setelah kita mendefinisikan skema, kita dapat dengan mudah melampirkan skema ke model pengguna.

 const userModel = mongoose.model('Users', userSchema);

Setelah itu, kita dapat menggunakan model ini untuk mengimplementasikan semua operasi CRUD yang kita inginkan dalam titik akhir Express kita.

Mari kita mulai dengan operasi "buat pengguna" dengan mendefinisikan rute di users/routes.config.js :

 app.post('/users', [ UsersController.insert ]);

Ini ditarik ke aplikasi Express kami di file index.js utama. Objek UsersController diimpor dari controller kami, di mana kami meng-hash kata sandi dengan tepat, yang didefinisikan di /users/controllers/users.controller.js :

 exports.insert = (req, res) => { let salt = crypto.randomBytes(16).toString('base64'); let hash = crypto.createHmac('sha512',salt) .update(req.body.password) .digest("base64"); req.body.password = salt + "$" + hash; req.body.permissionLevel = 1; UserModel.createUser(req.body) .then((result) => { res.status(201).send({id: result._id}); }); };

Pada titik ini, kita dapat menguji model Mongoose kita dengan menjalankan server ( npm start ) dan mengirimkan permintaan POST ke /users dengan beberapa data JSON:

 { "firstName" : "Marcos", "lastName" : "Silva", "email" : "[email protected]", "password" : "s3cr3tp4sswo4rd" }

Ada beberapa alat yang dapat Anda gunakan untuk ini. Insomnia (dibahas di bawah) dan Postman adalah alat GUI yang populer, dan curl adalah pilihan CLI yang umum. Anda bahkan dapat menggunakan JavaScript, misalnya, dari konsol alat pengembangan bawaan browser Anda:

 fetch('http://localhost:3600/users', { method: 'POST', headers: { "Content-type": "application/json" }, body: JSON.stringify({ "firstName": "Marcos", "lastName": "Silva", "email": "[email protected]", "password": "s3cr3tp4sswo4rd" }) }) .then(function(response) { return response.json(); }) .then(function(data) { console.log('Request succeeded with JSON response', data); }) .catch(function(error) { console.log('Request failed', error); });

Pada titik ini, hasil dari posting yang valid hanya akan berupa id dari pengguna yang dibuat: { "id": "5b02c5c84817bf28049e58a3" } . Kita juga perlu menambahkan metode createUser ke model di users/models/users.model.js :

 exports.createUser = (userData) => { const user = new User(userData); return user.save(); };

Semua siap, sekarang kita perlu melihat apakah pengguna itu ada. Untuk itu, kita akan mengimplementasikan fitur “get user by id” untuk endpoint berikut: users/:userId .

Pertama, kita membuat rute di /users/routes/config.js :

 app.get('/users/:userId', [ UsersController.getById ]);

Kemudian, kita membuat controller di /users/controllers/users.controller.js :

 exports.getById = (req, res) => { UserModel.findById(req.params.userId).then((result) => { res.status(200).send(result); }); };

Dan terakhir, tambahkan metode findById ke model di /users/models/users.model.js :

 exports.findById = (id) => { return User.findById(id).then((result) => { result = result.toJSON(); delete result._id; delete result.__v; return result; }); };

Responsnya akan seperti ini:

 { "firstName": "Marcos", "lastName": "Silva", "email": "[email protected]", "password": "Y+XZEaR7J8xAQCc37nf1rw==$p8b5ykUx6xpC6k8MryDaRmXDxncLumU9mEVabyLdpotO66Qjh0igVOVerdqAh+CUQ4n/E0z48mp8SDTpX2ivuQ==", "permissionLevel": 1, "id": "5b02c5c84817bf28049e58a3" }

Perhatikan bahwa kita dapat melihat kata sandi yang di-hash. Untuk tutorial ini, kami menunjukkan kata sandi, tetapi praktik terbaik yang jelas adalah jangan pernah mengungkapkan kata sandi, meskipun telah di-hash. Hal lain yang bisa kita lihat adalah permissionLevel , yang akan kita gunakan untuk menangani izin pengguna nanti.

Mengulangi pola yang dijelaskan di atas, sekarang kita dapat menambahkan fungsionalitas untuk memperbarui pengguna. Kami akan menggunakan operasi PATCH karena itu akan memungkinkan kami untuk mengirim hanya bidang yang ingin kami ubah. Oleh karena itu, rute akan menjadi PATCH ke /users/:userid , dan kami akan mengirimkan bidang apa pun yang ingin kami ubah. Kami juga perlu menerapkan beberapa validasi tambahan karena perubahan harus dibatasi untuk pengguna yang bersangkutan atau admin, dan hanya admin yang dapat mengubah permissionLevel . Kami akan melewatkannya untuk saat ini dan kembali ke sana setelah kami mengimplementasikan modul auth. Untuk saat ini, pengontrol kami akan terlihat seperti ini:

 exports.patchById = (req, res) => { if (req.body.password){ let salt = crypto.randomBytes(16).toString('base64'); let hash = crypto.createHmac('sha512', salt).update(req.body.password).digest("base64"); req.body.password = salt + "$" + hash; } UserModel.patchUser(req.params.userId, req.body).then((result) => { res.status(204).send({}); }); };

Secara default, kami akan mengirimkan kode HTTP 204 tanpa isi respons untuk menunjukkan bahwa permintaan berhasil.

Dan kita perlu menambahkan metode patchUser ke model:

 exports.patchUser = (id, userData) => { return User.findOneAndUpdate({ _id: id }, userData); };

Daftar pengguna akan diimplementasikan sebagai GET di /users/ oleh pengontrol berikut:

 exports.list = (req, res) => { let limit = req.query.limit && req.query.limit <= 100 ? parseInt(req.query.limit) : 10; let page = 0; if (req.query) { if (req.query.page) { req.query.page = parseInt(req.query.page); page = Number.isInteger(req.query.page) ? req.query.page : 0; } } UserModel.list(limit, page).then((result) => { res.status(200).send(result); }) };

Metode model yang sesuai adalah:

 exports.list = (perPage, page) => { return new Promise((resolve, reject) => { User.find() .limit(perPage) .skip(perPage * page) .exec(function (err, users) { if (err) { reject(err); } else { resolve(users); } }) }); };

Respons daftar yang dihasilkan akan memiliki struktur berikut:

 [ { "firstName": "Marco", "lastName": "Silva", "email": "[email protected]", "password": "z4tS/DtiH+0Gb4J6QN1K3w==$al6sGxKBKqxRQkDmhnhQpEB6+DQgDRH2qr47BZcqLm4/fphZ7+a9U+HhxsNaSnGB2l05Oem/BLIOkbtOuw1tXA==", "permissionLevel": 1, "id": "5b02c5c84817bf28049e58a3" }, { "firstName": "Paulo", "lastName": "Silva", "email": "[email protected]", "password": "wTsqO1kHuVisfDIcgl5YmQ==$cw7RntNrNBNw3MO2qLbx959xDvvrDu4xjpYfYgYMxRVDcxUUEgulTlNSBJjiDtJ1C85YimkMlYruU59rx2zbCw==", "permissionLevel": 1, "id": "5b02d038b653603d1ca69729" } ]

Dan bagian terakhir yang akan diimplementasikan adalah DELETE di /users/:userId .

Pengontrol kami untuk dihapus adalah:

 exports.removeById = (req, res) => { UserModel.removeById(req.params.userId) .then((result)=>{ res.status(204).send({}); }); };

Sama seperti sebelumnya, pengontrol akan mengembalikan kode HTTP 204 dan tidak ada isi konten sebagai konfirmasi.

Metode model yang sesuai akan terlihat seperti ini:

 exports.removeById = (userId) => { return new Promise((resolve, reject) => { User.deleteMany({_id: userId}, (err) => { if (err) { reject(err); } else { resolve(err); } }); }); };

Kami sekarang memiliki semua operasi yang diperlukan untuk memanipulasi sumber daya pengguna, dan kami selesai dengan pengontrol pengguna. Ide utama dari kode ini adalah untuk memberi Anda konsep inti menggunakan pola REST. Kita harus kembali ke kode ini untuk menerapkan beberapa validasi dan izin, tetapi pertama-tama, kita harus mulai membangun keamanan kita. Mari kita buat modul auth.

Membuat Modul Otentikasi

Sebelum kita dapat mengamankan modul users dengan menerapkan middleware izin dan validasi, kita harus dapat membuat token yang valid untuk pengguna saat ini. Kami akan menghasilkan JWT sebagai tanggapan atas pengguna yang memberikan email dan kata sandi yang valid. JWT adalah token web JSON luar biasa yang dapat Anda gunakan agar pengguna membuat beberapa permintaan dengan aman tanpa memvalidasi berulang kali. Biasanya memiliki waktu kedaluwarsa, dan token baru dibuat ulang setiap beberapa menit untuk menjaga komunikasi tetap aman. Namun, untuk tutorial ini, kami tidak akan memperbarui token dan membuatnya tetap sederhana dengan satu token per login.

Pertama, kita akan membuat titik akhir untuk permintaan POST ke /auth resource. Badan permintaan akan berisi email pengguna dan kata sandi:

 { "email" : "[email protected]", "password" : "s3cr3tp4sswo4rd2" }

Sebelum kita menggunakan controller, kita harus memvalidasi pengguna di /authorization/middlewares/verify.user.middleware.js :

 exports.isPasswordAndUserMatch = (req, res, next) => { UserModel.findByEmail(req.body.email) .then((user)=>{ if(!user[0]){ res.status(404).send({}); }else{ let passwordFields = user[0].password.split('$'); let salt = passwordFields[0]; let hash = crypto.createHmac('sha512', salt).update(req.body.password).digest("base64"); if (hash === passwordFields[1]) { req.body = { userId: user[0]._id, email: user[0].email, permissionLevel: user[0].permissionLevel, provider: 'email', name: user[0].firstName + ' ' + user[0].lastName, }; return next(); } else { return res.status(400).send({errors: ['Invalid email or password']}); } } }); };

Setelah melakukan itu, kita dapat melanjutkan ke controller dan menghasilkan JWT:

 exports.login = (req, res) => { try { let refreshId = req.body.userId + jwtSecret; let salt = crypto.randomBytes(16).toString('base64'); let hash = crypto.createHmac('sha512', salt).update(refreshId).digest("base64"); req.body.refreshKey = salt; let token = jwt.sign(req.body, jwtSecret); let b = Buffer.from(hash); let refresh_token = b.toString('base64'); res.status(201).send({accessToken: token, refreshToken: refresh_token}); } catch (err) { res.status(500).send({errors: err}); } };

Meskipun kita tidak akan me-refresh token dalam tutorial ini, controller telah diatur untuk mengaktifkan generasi tersebut agar lebih mudah untuk mengimplementasikannya dalam pengembangan selanjutnya.

Yang kita butuhkan sekarang adalah membuat rute dan memanggil middleware yang sesuai di /authorization/routes.config.js :

 app.post('/auth', [ VerifyUserMiddleware.hasAuthValidFields, VerifyUserMiddleware.isPasswordAndUserMatch, AuthorizationController.login ]);

Respons akan berisi JWT yang dihasilkan di bidang accessToken:

 { "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI1YjAyYzVjODQ4MTdiZjI4MDQ5ZTU4YTMiLCJlbWFpbCI6Im1hcmNvcy5oZW5yaXF1ZUB0b3B0YWwuY29tIiwicGVybWlzc2lvbkxldmVsIjoxLCJwcm92aWRlciI6ImVtYWlsIiwibmFtZSI6Ik1hcmNvIFNpbHZhIiwicmVmcmVzaF9rZXkiOiJiclhZUHFsbUlBcE1PakZIRG1FeENRPT0iLCJpYXQiOjE1MjY5MjMzMDl9.mmNg-i44VQlUEWP3YIAYXVO-74803v1mu-y9QPUQ5VY", "refreshToken": "U3BDQXBWS3kyaHNDaGJNanlJTlFkSXhLMmFHMzA2NzRsUy9Sd2J0YVNDTmUva0pIQ0NwbTJqOU5YZHgxeE12NXVlOUhnMzBWMGNyWmdOTUhSaTdyOGc9PQ==" }

Setelah membuat token, kita dapat menggunakannya di dalam header Authorization menggunakan formulir Bearer ACCESS_TOKEN .

Membuat Izin dan Validasi Middleware

Hal pertama yang harus kita definisikan adalah siapa yang dapat menggunakan sumber daya users . Ini adalah skenario yang harus kita tangani:

  • Publik untuk membuat pengguna (proses pendaftaran). Kami tidak akan menggunakan JWT untuk skenario ini.
  • Pribadi untuk pengguna yang masuk dan bagi admin untuk memperbarui pengguna itu.
  • Pribadi untuk admin hanya untuk menghapus akun pengguna.

Setelah mengidentifikasi skenario ini, pertama-tama kami akan membutuhkan middleware yang selalu memvalidasi pengguna jika mereka menggunakan JWT yang valid. Middleware di /common/middlewares/auth.validation.middleware.js bisa sesederhana:

 exports.validJWTNeeded = (req, res, next) => { if (req.headers['authorization']) { try { let authorization = req.headers['authorization'].split(' '); if (authorization[0] !== 'Bearer') { return res.status(401).send(); } else { req.jwt = jwt.verify(authorization[1], secret); return next(); } } catch (err) { return res.status(403).send(); } } else { return res.status(401).send(); } };

Kami akan menggunakan kode kesalahan HTTP untuk menangani kesalahan permintaan:

  • HTTP 401 untuk permintaan yang tidak valid
  • HTTP 403 untuk permintaan yang valid dengan token yang tidak valid, atau token yang valid dengan izin yang tidak valid

Kita dapat menggunakan operator AND bitwise (bitmasking) untuk mengontrol hak akses. Jika kita menetapkan setiap izin yang diperlukan sebagai pangkat 2, kita dapat memperlakukan setiap bit bilangan bulat 32-bit sebagai izin tunggal. Admin kemudian dapat memiliki semua izin dengan menyetel nilai izinnya ke 2147483647. Pengguna tersebut kemudian dapat memiliki akses ke rute apa pun. Sebagai contoh lain, pengguna yang nilai izinnya disetel ke 7 akan memiliki izin untuk peran yang ditandai dengan bit untuk nilai 1, 2, dan 4 (dua pangkat 0, 1, dan 2).

Middleware untuk itu akan terlihat seperti ini:

 exports.minimumPermissionLevelRequired = (required_permission_level) => { return (req, res, next) => { let user_permission_level = parseInt(req.jwt.permission_level); let user_id = req.jwt.user_id; if (user_permission_level & required_permission_level) { return next(); } else { return res.status(403).send(); } }; };

Middleware bersifat umum. Jika tingkat izin pengguna dan tingkat izin yang diperlukan bertepatan dalam setidaknya satu bit, hasilnya akan lebih besar dari nol, dan kita dapat membiarkan tindakan berlanjut; jika tidak, kode HTTP 403 akan dikembalikan.

Sekarang, kita perlu menambahkan middleware otentikasi ke rute modul pengguna di /users/routes.config.js :

 app.post('/users', [ UsersController.insert ]); app.get('/users', [ ValidationMiddleware.validJWTNeeded, PermissionMiddleware.minimumPermissionLevelRequired(PAID), UsersController.list ]); app.get('/users/:userId', [ ValidationMiddleware.validJWTNeeded, PermissionMiddleware.minimumPermissionLevelRequired(FREE), PermissionMiddleware.onlySameUserOrAdminCanDoThisAction, UsersController.getById ]); app.patch('/users/:userId', [ ValidationMiddleware.validJWTNeeded, PermissionMiddleware.minimumPermissionLevelRequired(FREE), PermissionMiddleware.onlySameUserOrAdminCanDoThisAction, UsersController.patchById ]); app.delete('/users/:userId', [ ValidationMiddleware.validJWTNeeded, PermissionMiddleware.minimumPermissionLevelRequired(ADMIN), UsersController.removeById ]);

Ini menyimpulkan pengembangan dasar REST API kami. Yang harus dilakukan hanyalah menguji semuanya.

Menjalankan dan Menguji dengan Insomnia

Insomnia adalah klien REST yang layak dengan versi gratis yang bagus. Praktik terbaik, tentu saja, menyertakan pengujian kode dan menerapkan pelaporan kesalahan yang tepat dalam proyek, tetapi klien REST pihak ketiga sangat bagus untuk menguji dan menerapkan solusi pihak ketiga ketika pelaporan kesalahan dan debugging layanan tidak tersedia. Kami akan menggunakannya di sini untuk memainkan peran aplikasi dan mendapatkan beberapa wawasan tentang apa yang terjadi dengan API kami.

Untuk membuat pengguna, kita hanya perlu POST bidang yang diperlukan ke titik akhir yang sesuai dan menyimpan ID yang dihasilkan untuk penggunaan selanjutnya.

Permintaan dengan data yang sesuai untuk membuat pengguna

API akan merespons dengan ID pengguna:

Respons konfirmasi dengan userID

Kita sekarang dapat menghasilkan JWT menggunakan titik akhir /auth/ :

Permintaan dengan data login

Kami harus mendapatkan token sebagai tanggapan kami:

Konfirmasi yang berisi Token Web JSON yang sesuai

Ambil accessToken , awali dengan Bearer (ingat spasi), dan tambahkan ke header permintaan di bawah Authorization :

Menyiapkan header untuk mentransfer berisi JWT otentikasi

Jika kita tidak melakukan ini sekarang karena kita telah menerapkan izin middleware, setiap permintaan selain pendaftaran akan mengembalikan kode HTTP 401. Namun, dengan token yang valid, kita mendapatkan respons berikut dari /users/:userId :

Tanggapan yang mencantumkan data untuk pengguna yang ditunjukkan

Juga, seperti yang telah disebutkan sebelumnya, kami menampilkan semua bidang, untuk tujuan pendidikan dan demi kesederhanaan. Kata sandi (di-hash atau lainnya) tidak boleh terlihat dalam respons.

Mari kita coba untuk mendapatkan daftar pengguna:

Permintaan daftar semua pengguna

Kejutan! Kami mendapat respons 403.

Tindakan ditolak karena kurangnya tingkat izin yang sesuai

Pengguna kami tidak memiliki izin untuk mengakses titik akhir ini. Kami perlu mengubah tingkat permissionLevel pengguna kami dari 1 menjadi 7 (atau bahkan 5 akan melakukannya, karena tingkat izin gratis dan berbayar kami masing-masing diwakili sebagai 1 dan 4.) Kami dapat melakukannya secara manual di MongoDB, pada prompt interaktifnya , seperti ini (dengan ID diubah ke hasil lokal Anda):

 db.users.update({"_id" : ObjectId("5b02c5c84817bf28049e58a3")},{$set:{"permissionLevel":5}})

Kemudian, kita perlu membuat JWT baru.

Setelah itu selesai, kami mendapatkan respons yang tepat:

Respon dengan semua pengguna dan datanya

Selanjutnya, mari kita uji fungsionalitas pembaruan dengan mengirimkan permintaan PATCH dengan beberapa bidang ke titik akhir /users/:userId kami:

Permintaan yang berisi sebagian data untuk diperbarui

Kami mengharapkan respons 204 sebagai konfirmasi operasi yang berhasil, tetapi kami dapat meminta pengguna sekali lagi untuk memverifikasi.

Respons setelah perubahan berhasil

Terakhir, kita perlu menghapus pengguna. Kita harus membuat pengguna baru seperti yang dijelaskan di atas (jangan lupa untuk mencatat ID pengguna) dan pastikan bahwa kita memiliki JWT yang sesuai untuk pengguna admin. Pengguna baru akan memerlukan izin mereka yang disetel ke 2053 (yaitu 2048— ADMIN —ditambah 5 sebelumnya) untuk dapat juga melakukan operasi penghapusan. Setelah itu selesai dan JWT baru dibuat, kami harus memperbarui header permintaan Authorization kami:

Minta penyiapan untuk menghapus pengguna

Mengirim permintaan DELETE ke /users/:userId , kita harus mendapatkan respons 204 sebagai konfirmasi. Kita dapat, sekali lagi, memverifikasi dengan meminta /users/ untuk mencantumkan semua pengguna yang ada.

Langkah Selanjutnya untuk REST API Anda

Dengan alat dan metode yang tercakup dalam tutorial ini, Anda seharusnya sekarang dapat membuat REST API yang sederhana dan aman di Node.js. Banyak praktik terbaik yang tidak penting untuk proses tersebut dilewati, jadi jangan lupa untuk:

  • Terapkan validasi yang tepat (misalnya, pastikan email pengguna unik)
  • Terapkan pengujian unit dan pelaporan kesalahan
  • Cegah pengguna mengubah tingkat izin mereka sendiri
  • Cegah admin menghapus diri mereka sendiri
  • Mencegah pengungkapan informasi sensitif (misalnya, kata sandi yang di-hash)
  • Pindahkan rahasia JWT dari common/config/env.config.js ke mekanisme distribusi rahasia off-repo berbasis non-lingkungan

Satu latihan terakhir bagi pembaca adalah mengubah basis kode dari penggunaan janji JavaScript ke teknik async/await.

Bagi Anda yang mungkin tertarik, sekarang juga tersedia versi TypeScript dari proyek tersebut.

Terkait: 5 Hal yang Belum Pernah Anda Lakukan dengan Spesifikasi REST