Jadi kali ini saya akan membuat API Server menggunakan NuxtJS dan Express yang mana dalam fitur NuxtJS bisa membuat backend dan fronted berjalan secara bersamaan, yakni express sebagai API dan NuxtJS sebagai frontend menggunakan “serverMiddleware”.
BACKEND
- Buat Project Baru
npx create-nuxt-app project1
Pilih UI framework : Bootstrap Vue
Pilih custom server framework : Express
Pilih NuxtJS modules : axios
- Install package
cd project1
npm install express-validator jsonwebtoken mongoose bcryptjs
Penjelasan :
express-validator : untuk validasi data yang di submit oleh form
jsonwebtoken : untuk generate dan verifikasi token login
mongoose : schema based untuk interaksi dengan mongodb
bcryptjs : untuk encode/decode autentikasi token (jsonwebtoken)
- Configurasi Nuxt
isikan code ini supaya bisa both backend dan frontend pada nuxtconfig.js dijelaskan bahwa server API nya ada di folder /api/index.js
serverMiddleware: [
'~/api/index.js'
]
- Mulai implementasinya
/api
–> /controllers – all the business logic for all individual modules
–> /models – create mongodb schema and define them in this folder in separate files for every schema
–> /routes – create API routes for every models created inside /models folder
–> db.js – all the db connection related stuff will be in this file
–> index.js – main file to run the API server
–> config.js – to store and access all global variables & functions. e.g. authentication token, checkAuthenticated() etc.
codingan /api/db.js
const mongoose = require('mongoose');
// mongodb database connection string. change it as per your needs. here "mydb" is the name of the database. You don't need to create DB from mongodb terminal. mongoose create the db automatically.
mongoose.connect('mongodb://localhost/mydb', {
useNewUrlParser: true,
useUnifiedTopology: true,
useFindAndModify: false,
useCreateIndex: true
});
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function callback () {
console.log("MongoDB Connected...");
});
module.exports = db
codingan /api/config.js
const jwt = require('jsonwebtoken');
const config = {
authSecret:'mysecret', // secret for generating jwt token
}
module.exports = config
// check if user logged in
module.exports.isAuthenticated = function (req, res, next) {
var token = req.headers.authorization
if (token) {
// verifies secret and checks if the token is expired
jwt.verify(token.replace(/^Bearer\s/, ''), config.authSecret, function(err, decoded) {
if (err) {
return res.status(401).json({message: 'unauthorized'})
} else {
return next();
}
});
}
else{
return res.status(401).json({message: 'unauthorized'})
}
}
codingan /api/index.js
const express = require('express')
const db = require('./db')
// Create express instnace
const app = express()
// Init body-parser options (inbuilt with express)
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// Require & Import API routes
const users = require('./routes/users')
const articles = require('./routes/articles')
// Use API Routes
app.use(users)
app.use(articles)
// Export the server middleware
module.exports = {
path: '/api',
handler: app
}
- Membuat Model Schema
Pada langkah ini kita mendefinisikan skema MongoDB. Paket mongoose membaca kode ini dan membuat skema secara otomatis di dalam MongoDB. Jadi, kita tidak perlu membuat skema apa pun dengan masuk ke MongoDB secara manual.
codingan /api/models/Article.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const Article = new Schema ({
title: { type: String, required: true, index: { unique: true } },
author: { type: String, required: true },
body: { type: String, required: true },
});
module.exports = mongoose.model('Article', Article)
codingan /api/models/User.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const User = new Schema ({
full_name: { type: String, required: true },
email: { type: String, required: true, index: { unique: true } },
password: { type: String, required: true },
});
module.exports = mongoose.model('User', User)
- Membuat Controller untuk setiap Model
codingan /api/controllers/usersController.js
const config = require('../config')
const User = require('../models/User')
const validator = require('express-validator')
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs')
// Register
module.exports.register = [
// validations rules
validator.body('full_name', 'Please enter Full Name').isLength({ min: 1 }),
validator.body('email', 'Please enter Email').isLength({ min: 1 }),
validator.body('email').custom(value => {
return User.findOne({email:value}).then(user => {
if (user !== null) {
return Promise.reject('Email already in use');
}
})
}),
validator.body('password', 'Please enter Password').isLength({ min: 1 }),
function(req, res) {
// throw validation errors
const errors = validator.validationResult(req);
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.mapped() });
}
// initialize record
var user = new User({
full_name : req.body.full_name,
email : req.body.email,
password : req.body.password,
})
// encrypt password
var salt = bcrypt.genSaltSync(10);
var hash = bcrypt.hashSync(user.password, salt);
user.password = hash
// save record
user.save(function(err, user){
if(err) {
return res.status(500).json({
message: 'Error saving record',
error: err
});
}
return res.json({
message: 'saved',
_id: user._id
});
})
}
]
// Login
module.exports.login = [
// validation rules
validator.body('email', 'Please enter Email').isLength({ min: 1 }),
validator.body('password', 'Please enter Password').isLength({ min: 1 }),
function(req, res) {
// throw validation errors
const errors = validator.validationResult(req);
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.mapped() });
}
// validate email and password are correct
User.findOne({email: req.body.email}, function(err, user){
if(err) {
return res.status(500).json({
message: 'Error logging in',
error: err
});
}
if (user === null) {
return res.status(500).json({
message: 'Email address you entered is not found.'
});
}
// compare submitted password with password inside db
return bcrypt.compare(req.body.password, user.password, function(err, isMatched) {
if(isMatched===true){
return res.json({
user: {
_id: user._id,
email: user.email,
full_name: user.full_name
},
token: jwt.sign({_id: user._id, email: user.email, full_name: user.full_name}, config.authSecret) // generate JWT token here
});
}
else{
return res.status(500).json({
message: 'Invalid Email or Password entered.'
});
}
});
});
}
]
// Get User
module.exports.user = function(req, res) {
var token = req.headers.authorization
if (token) {
// verifies secret and checks if the token is expired
jwt.verify(token.replace(/^Bearer\s/, ''), config.authSecret, function(err, decoded) {
if (err) {
return res.status(401).json({message: 'unauthorized'})
} else {
return res.json({ user: decoded })
}
});
}
else{
return res.status(401).json({message: 'unauthorized'})
}
}
codingan /api/controllers/articlesController.js
const Article = require('../models/Article');
const validator = require('express-validator');
// Get all
module.exports.list = function (req, res, next) {
Article.find({}, function(err, articles){
if(err) {
return res.status(500).json({
message: 'Error getting records.'
});
}
return res.json(articles);
});
}
// Get one
module.exports.show = function(req, res) {
var id = req.params.id;
Article.findOne({_id: id}, function(err, article){
if(err) {
return res.status(500).json({
message: 'Error getting record.'
});
}
if(!article) {
return res.status(404).json({
message: 'No such record'
});
}
return res.json(article);
});
}
// Create
module.exports.create = [
// validations rules
validator.body('title', 'Please enter Article Title').isLength({ min: 1 }),
validator.body('title').custom(value => {
return Article.findOne({title:value}).then(article => {
if (article !== null) {
return Promise.reject('Title already in use');
}
})
}),
validator.body('author', 'Please enter Author Name').isLength({ min: 1 }),
validator.body('body', 'Please enter Article Content').isLength({ min: 1 }),
function(req, res) {
// throw validation errors
const errors = validator.validationResult(req);
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.mapped() });
}
// initialize record
var article = new Article({
title : req.body.title,
author : req.body.author,
body : req.body.body,
})
// save record
article.save(function(err, article){
if(err) {
return res.status(500).json({
message: 'Error saving record',
error: err
});
}
return res.json({
message: 'saved',
_id: article._id
});
})
}
]
// Update
module.exports.update = [
// validation rules
validator.body('title', 'Please enter Article Title').isLength({ min: 1 }),
validator.body('title').custom( (value, {req}) => {
return Article.findOne({ title:value, _id:{ $ne: req.params.id } })
.then( article => {
if (article !== null) {
return Promise.reject('Title already in use');
}
})
}),
validator.body('author', 'Please enter Author Name').isLength({ min: 1 }),
validator.body('body', 'Please enter Article Content').isLength({ min: 1 }),
function(req, res) {
// throw validation errors
const errors = validator.validationResult(req);
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.mapped() });
}
var id = req.params.id;
Article.findOne({_id: id}, function(err, article){
if(err) {
return res.status(500).json({
message: 'Error saving record',
error: err
});
}
if(!article) {
return res.status(404).json({
message: 'No such record'
});
}
// initialize record
article.title = req.body.title ? req.body.title : article.title;
article.author = req.body.author ? req.body.author : article.author;
article.body = req.body.body ? req.body.body : article.body;
// save record
article.save(function(err, article){
if(err) {
return res.status(500).json({
message: 'Error getting record.'
});
}
if(!article) {
return res.status(404).json({
message: 'No such record'
});
}
return res.json(article);
});
});
}
]
// Delete
module.exports.delete = function(req, res) {
var id = req.params.id;
Article.findByIdAndRemove(id, function(err, article){
if(err) {
return res.status(500).json({
message: 'Error getting record.'
});
}
return res.json(article);
});
}
- Buat Routes untuk setiap Controller
codingan /api/routes/users.js
const config = require('../config')
const { Router } = require('express')
const router = Router()
// Initialize Controller
const usersController = require('../controllers/usersController')
// Register
router.post('/users/register', usersController.register)
// Login
router.post('/users/login', usersController.login)
// Get User
router.get('/users/user', usersController.user)
module.exports = router
codingan /api/routes/articles.js
const config = require('../config')
const { Router } = require('express')
const router = Router()
// Initialize Controller
const articlesController = require('../controllers/articlesController')
// Get All
router.get('/articles', articlesController.list)
// Get One
router.get('/articles/:id', articlesController.show)
// Create
router.post('/articles', config.isAuthenticated, articlesController.create)
// Update
router.put('/articles/:id', config.isAuthenticated, articlesController.update)
// Delete
router.delete('/articles/:id', config.isAuthenticated, articlesController.delete)
module.exports = router
Perhatikan “config.isAuthenticated” dalam kode ini. Ini adalah middleware yang digunakan untuk mengamankan rute tertentu.
berikut adalah endpointsnya:
[POST] /api/users/register => register user
[POST] /api/users/login => log in user
[GET] /api/users/user => get logged in user details [GET] /api/articles => get all articles
[GET] /api/articles/:id => get single article
[POST] /api/articles => create article
[PUT] /api/articles/:id => update article
[DELETE] /api/articles/:id => delete article
- Jalankan server
npm run dev
sekarang test endpointnya pakai imsonia dan pastikan sudah sesuai semua.
FRONTEND
- Install Nuxt Auth Module
Modul Nuxt Auth adalah modul Nuxt resmi yang dapat kita gunakan untuk mengimplementasikan fitur terkait Autentikasi Pengguna.
cd nuxt-with-express
npm install @nuxtjs/auth
- Konfigurasi Nuxt Modules
taruh didalam /nuxt.config.js untuk mengaktifkan modulenya
modules: [
'bootstrap-vue/nuxt', // enables bootstrap vue module
'@nuxtjs/axios', // enables Nuxt Axios module
'@nuxtjs/auth', // enables Nuxt Auth module
],
- Konfigurasi Nuxt Auth
tambah lagi didalam file nuxt confignya
auth: {
strategies: {
local: {
endpoints: {
// these are the API endpoints we created in Express
login: {
url: '/api/users/login',
method: 'post',
propertyName: 'token'
},
logout: true,
user: {
url: '/api/users/user',
method: 'get',
propertyName: 'user'
}
},
tokenRequired: true,
tokenType: "Bearer"
}
},
redirect: {
login: '/user/login', // User will be redirected to this path if login is required
logout: '/', // User will be redirected to this path if after logout, current route is protected
home: '/' // User will be redirect to this path after login if accessed login page directly
},
rewriteRedirects: true,
},
https://github.com/aslamdoctor/nuxt-with-express/blob/master/layouts/default.vue
Github :
https://github.com/aslamdoctor/nuxt-with-express/tree/master/pages/articles
https://github.com/aslamdoctor/nuxt-with-express/blob/master/pages/index.vue
npm run dev
buka url http://localhost:3000
- Selesai
Nice!!!
CRUD Nuxt Mongo – Kresec ::
CRUD Nuxt Mongo – Kresec ::
Great!
Great!
Great!
CRUD Nuxt Mongo – Kresec ::