1. Code
  2. JavaScript
  3. Node

Membangun Aplikasi Web Menggunakan Node.js

Scroll to top

Indonesian (Bahasa Indonesia) translation by Meyria (you can also view the original English article)

Pengenalan

Selain membangun API, Node.js sangat bagus untuk membangun aplikasi web standar. Ini memiliki alat yang hebat untuk memenuhi selera pengembang web. Dalam tutorial ini, Anda akan membangun aplikasi web yang bisa berfungsi sebagai perpustakaan lokal.

Sementara membangun Anda akan belajar tentang beberapa jenis middleware, Anda akan melihat bagaimana menangani pengiriman form di Node.js, dan Anda juga bisa merujuk dua model.

Mari memulai.

Memulai

Mulailah dengan memasang generator express pada mesin anda.

1
npm install express-generator -g

Jalankan perintah generator express untuk menghasilkan aplikasi anda.

1
express tutsplus-library --view=pug
1
   create : tutsplus-library
2
   create : tutsplus-library/package.json
3
   create : tutsplus-library/app.js
4
   create : tutsplus-library/public
5
   create : tutsplus-library/routes
6
   create : tutsplus-library/routes/index.js
7
   create : tutsplus-library/routes/users.js
8
   create : tutsplus-library/views
9
   create : tutsplus-library/views/index.pug
10
   create : tutsplus-library/views/layout.pug
11
   create : tutsplus-library/views/error.pug
12
   create : tutsplus-library/bin
13
   create : tutsplus-library/bin/www
14
   create : tutsplus-library/public/javascripts
15
   create : tutsplus-library/public/images
16
   create : tutsplus-library/public/stylesheets
17
   create : tutsplus-library/public/stylesheets/style.css
18
19
   install dependencies:
20
     $ cd tutsplus-library && npm install

21


22
   run the app:
23
     $ DEBUG=tutsplus-library:* npm start

Sekarang bermigrasi ke pekerjaan Anda, buka package.json, dan buat dependensi yang sama dengan yang saya miliki di bawah ini.

1
#package.json
2
3
{
4
  "name": "tutsplus-library",
5
  "version": "0.0.0",
6
  "private": true,
7
  "scripts": {
8
    "start": "node ./bin/www"
9
  },
10
  "dependencies": {
11
    "body-parser": "~1.17.1",
12
    "connect-flash": "^0.1.1",
13
    "cookie-parser": "~1.4.3",
14
    "debug": "~2.6.3",
15
    "express": "~4.15.2",
16
    "express-messages": "^1.0.1",
17
    "express-session": "^1.15.5",
18
    "express-validator": "^4.2.1",
19
    "mongoose": "^4.11.12",
20
    "morgan": "~1.8.1",
21
    "pug": "~2.0.0-beta11",
22
    "serve-favicon": "~2.4.2"
23
  }
24
}

Jalankan perintah untuk menginstal paket.

1
npm install

Siapkan file entri

app.js dibuat saat Anda menjalankan perintah generator; Namun, Anda perlu mengatur konfigurasi ekstra. Edit file agar terlihat seperti yang saya miliki di bawah ini.

1
#app.js
2
3
var express = require('express');
4
var path = require('path');
5
var favicon = require('serve-favicon');
6
var logger = require('morgan');
7
var cookieParser = require('cookie-parser');
8
var bodyParser = require('body-parser');
9
const session = require('express-session')
10
const expressValidator = require('express-validator')
11
const flash = require('connect-flash')
12
const mongoose = require('mongoose')
13
14
// 1

15
const genres = require('./routes/genres');
16
const books = require('./routes/books');
17
18
var app = express();
19
20
// 2

21
mongoose.Promise = global.Promise
22
const mongoDB = process.env.MONGODB_URI || 'mongodb://127.0.0.1/tutsplus-library'
23
mongoose.connect(mongoDB)
24
25
// view engine setup

26
app.set('views', path.join(__dirname, 'views'));
27
app.set('view engine', 'pug');
28
29
// uncomment after placing your favicon in /public

30
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));

31
app.use(logger('dev'));
32
app.use(bodyParser.json());
33
app.use(bodyParser.urlencoded({ extended: false }));
34
app.use(cookieParser());
35
app.use(express.static(path.join(__dirname, 'public')));
36
37
// 3

38
app.use(session({
39
  secret: 'secret',
40
  saveUninitialized: true,
41
  resave: true
42
}))
43
44
// 4

45
app.use(expressValidator({
46
  errorFormatter: function(param, msg, value) {
47
    var namespace = param.split('.')
48
    , root = namespace.shift()
49
    , formParam = root
50
51
    while(namespace.length) {
52
      formParam += '[' + namespace.shift() + ']'
53
    }
54
    return {
55
      param : formParam,
56
      msg : msg,
57
      value : value
58
    }
59
  }
60
}))
61
62
// 5

63
app.use(flash())
64
app.use(function (req, res, next) {
65
  res.locals.messages = require('express-messages')
66
  next()
67
})
68
69
// 6

70
app.use('/genres', genres);
71
app.use('/books', books);
72
73
// catch 404 and forward to error handler

74
app.use(function(req, res, next) {
75
  var err = new Error('Not Found');
76
  err.status = 404;
77
  next(err);
78
});
79
80
// error handler

81
app.use(function(err, req, res, next) {
82
  // set locals, only providing error in development

83
  res.locals.message = err.message;
84
  res.locals.error = req.app.get('env') === 'development' ? err : {};
85
86
  // render the error page

87
  res.status(err.status || 500);
88
  res.render('error');
89
});
90
91
module.exports = app;
  1. Anda membutuhkan dua route yang akan Anda gunakan dalam membangun aplikasi ini. Anda akan segera membuat route file. Rute yang diperlukan ditetapkan sebagai nilai pada dua variabel berbeda yang digunakan saat menyiapkan middleware untuk route Anda.
  2. Anda menetapkan Mongoose untuk menggunakan global.Promise. Variabel MongoDB ditugaskan MONGODB_URI untuk lingkungan kerja anda atau jalan ke server mongo lokal anda. Variabel ini dilewatkan sebagai argumen untuk terhubung ke server MongoDB yang sedang berjalan.
  3. Anda menyiapkan session middleware menggunakan express-session. Middleware ini penting karena Anda akan menampilkan pesan sekejap di beberapa bagian aplikasi anda.
  4. Anda menyiapkan middleware untuk validasi. Middleware ini akan digunakan untuk memvalidasi form input, memastikan pengguna aplikasi tidak mengirimkan form kosong. Validasi menggunakan paket yang terinstal, express-validator.
  5. Anda menyiapkan middleware yang akan berguna saat menampilkan pesan kilat. Middleware ini menggunakan connect-flash.
  6. Route untuk aplikasi disiapkan untuk memanfaatkan file route yang anda butuhkan. Permintaan yang menunjuk ke /genres dan /books akan menggunakan genre dan buku yang mengarahkan file masing-masing. Pada saat ini Anda belum membuat file route, namun anda akan segera melakukannya.

Model Buku dan Genre

Model Buku akan menggunakan Skema Mongoose untuk menentukan bagaimana buku akan terstruktur. Buat direktori yang disebut models, dan sebuah file baru bernama Book.js. Begini tampilannya.

1
#models/Book.js
2
3
const mongoose = require('mongoose')
4
mongoose.Promise = global.Promise
5
const Schema = mongoose.Schema
6
7
const bookSchema = Schema({
8
  name: {
9
    type: String,
10
    trim: true,
11
    required: 'Please enter a book name'
12
  },
13
  description: {
14
    type: String,
15
    trim: true
16
  },
17
  author: {
18
    type: String,
19
    trim: true,
20
  },
21
  genre: [{
22
    type: Schema.Types.ObjectId,
23
    ref: 'Genre'
24
  }]
25
})
26
27
module.exports = mongoose.model('Book', bookSchema)

Di sini anda memiliki empat field. Field terakhir digunakan untuk menyimpan genre yang dimiliki setiap buku. Bidang genre di sini merujuk pada model Genre, yang akan dibuat selanjutnya. Itu sebabnya tipe ini diatur ke Schema.Types.ObjectId, di mana id dari masing-masing genre yang dirujuk akan disimpan. ref menentukan model yang Anda referensikan. Perhatikan bahwa genre disimpan sebagai array, artinya buku bisa memiliki lebih dari satu genre.

Mari kita segera membuat model Genre.

1
#models/genre.js
2
3
const mongoose = require('mongoose')
4
mongoose.Promise = global.Promise
5
const Schema = mongoose.Schema
6
7
const genreSchema = Schema({
8
  name: {
9
    type: String,
10
    trim: true,
11
    required: 'Please enter a Genre name'
12
  }
13
})
14
15
module.exports = mongoose.model('Genre', genreSchema)

Untuk Genre Anda, Anda hanya perlu satu field: nama.

Route Genre Index dan View

Untuk tutorial ini, Anda akan menggunakan dua jalur route untuk genre anda: jalur untuk menambahkan genre baru, dan satu lagi yang mencantumkan genre yang anda miliki. Buat file di direktori route Anda yang disebut genres.js.

Mulailah dengan membutuhkan semua modul yang akan anda gunakan.

1
#routes/genres.js
2
3
var express = require('express');
4
var router = express.Router();
5
const mongoose = require('mongoose')
6
const Genre = require('../models/Genre')

Selanjutnya, turunkan route yang menangani file index untuk genre anda.

1
router.get('/', (req, res, next) => {
2
  const genres = Genre.find({}).exec()
3
    .then((genres) => {
4
      res.render('genres', { genres: genres })
5
  }, (err) => {
6
    throw err
7
  })
8
});

Route ini akan dipanggil kapan pun permintaan diajukan ke /genres. Di sini Anda memanggil metode pencarian pada model Genre Anda untuk mendapatkan semua genre yang telah dibuat. Genre ini kemudian di-render pada template yang disebut genres. Ayo maju dan buat itu, tetapi pertama, perbarui layout.pug anda agar terlihat seperti ini:

1
#views/layout.pug
2
3
doctype html
4
html
5
  head
6
    title= title
7
    link(rel='stylesheet', href='/stylesheets/style.css')
8
    link(rel='stylesheet', href='https://bootswatch.com/paper/bootstrap.css')
9
    script(src='https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js')
10
    script(src='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js')
11
  body
12
    .container-fluid
13
      block header
14
        nav.navbar.navbar-inverse
15
          .container-fluid
16
            .navbar-header
17
              button.navbar-toggle.collapsed(type='button', data-toggle='collapse', data-target='#bs-example-navbar-collapse-2')
18
                span.sr-only Toggle navigation
19
                span.icon-bar
20
                span.icon-bar
21
                span.icon-bar
22
              a.navbar-brand(href='#') Local Library
23
            #bs-example-navbar-collapse-2.collapse.navbar-collapse
24
              ul.nav.navbar-nav.navbar-right
25
                li
26
                  a(href='/books') View Books
27
                li
28
                  a(href='/books/add') Add New Book
29
                li
30
                  a(href='/genres') View Genres
31
                li
32
                  a(href='/genres/add') Add New Genre
33
        block content

Ini akan memberi anda pandangan struktur yang bagus untuk membantu navigasi. Sekarang buat file view bernama genre.pug. Dalam file ini, anda akan melakukan perulangan melalui genre yang dibuat dan menampilkan setiap genre dalam daftar yang tidak berurutan.

Berikut adalah bagaimana file akan terlihat.

1
#views/genres.pug
2
3
extends layout
4
5
block content
6
  h1 Genre
7
  ul.well.well-lg
8
    each genre, i in genres
9
      li.well.well-sm
10
        p #{genre.name}

Tambahkan Route Genre Baru dan View

Kembali ke routes/genres.js untuk menambahkan route yang akan menangani pembuatan genre baru.

1
#routes/genres.js
2
3
// 1

4
router.get('/add', (req, res, next) => {
5
  res.render('addGenre')
6
})
7
8
// 2

9
router.post('/add', (req, res, next) => {
10
  req.checkBody('name', 'Name is required').notEmpty()
11
12
  const errors = req.validationErrors()
13
14
  if (errors) {
15
    console.log(errors)
16
    res.render('addgenres', { genre, errors })
17
  }
18
19
  const genre = (new Genre(req.body)).save()
20
    .then((data) => {
21
      res.redirect('/genres')
22
    })
23
    .catch((errors) => {
24
      console.log('oops...')
25
      console.log(errors)
26
    })
27
})
28
29
// 3

30
module.exports = router;
  1. Tugas router ini adalah dengan hanya menampilkan halaman untuk menambahkan route baru. Router ini dipanggil setiap kali permintaan dibuat ke jalur /genres/add.
  2. Router ini menangani pengajuan form. Saat form dikirimkan, kitai periksa untuk memastikan bahwa sebuah nama dimasukkan oleh pengguna. Jika tidak ada nama yang dimasukkan, halaman akan dirender-ulang. Jika diperiksa bagus, genre akan disimpan dan pengguna dialihkan ke halaman /genres.
  3. Modul ini diekspor sebagai sebuah router.

Sekarang snda bisa terus maju dan membuat halaman untuk menambahkan genre baru.

1
#views/addGenre.pug
2
3
extends layout
4
5
block content
6
  .row
7
    .col-md-12
8
      h1 Add Book
9
      form(method="POST", action="/genres/add")
10
        .form-group
11
          label.col-lg-2.control.label Name
12
          .col-lg-10
13
            input.form-control(type="text", name='name')
14
        .form-group
15
          .col-lg-10.col-lg-offset-2
16
            input.button.btn.btn-primary(type='submit', value='Submit')
17
      
18
      if errors
19
        ul
20
          for error in errors
21
            li!= error.msg

Route Buku dan View

Buat sebuah file route baru untuk buku, dan nama diberi nama books.js. Seperti yang anda lakukan sebelumnya dengan genre, mulailah dengan membutuhkan modul-modul yang diperlukan.

1
#routes/books.js
2
3
var express = require('express');
4
var router = express.Router();
5
const mongoose = require('mongoose')
6
const Book = require('../models/Book')
7
const Genre = require('../models/Genre')

Selanjutnya, mengatur router untuk menampilkan semua buku yang disimpan di Perpustakaan. Coba itu sendiri seperti anda mengaturnya pada genre; anda selalu dapat memeriksa kembali untuk melakukan koreksi.

Saya kira anda akan mencobanya—berikut adalah bagaimana hal ini akan terlihat.

1
router.get('/', (req, res, next) => {
2
  const books = Book.find({}).exec().then((books) => {
3
    res.render('books', { books: books })
4
  }, (err) => {
5
    throw err
6
  })
7
});

Ketika router ini dipanggil, permintaan dibuat untuk menemukan semua buku-buku yang disimpan dalam database. Jika semuanya berjalan lancar, buku-buku akan ditampilkan pada halaman /books, jika tidak sebuah error akan muncul.

Anda perlu membuat file baru untuk menampilkan semua buku, dan berikut adalah bagaimana hal ini akan terlihat.

1
#views/books.pug
2
3
extends layout
4
5
block content
6
  h1 Books
7
  ul.well.well-lg
8
    each book, i in books
9
      li.well.well-sm
10
        a(href=`/books/show/${book.id}`) #{book.name}
11
        p= book.description

Anda cukup melakukan perulangan melalui buku-buku yang dikembalikan dan mengeluarkan nama dan deskripsi dari setiap buku yang menggunakan daftar yang tidak berurutan. Nama buku mengarah ke halaman masing-masing buku.

Route Tambah Buku Baru dan View

Pada router berikutnya anda akan mengatur penanganan penambahan buku baru. Kedua router akan digunakan di sini: yang satu hanya akan membuat halaman, dan yang lain akan menangani pengajuan form.

Ini adalah bagaimana router terlihat.

1
router.get('/add', (req, res, next) => {
2
  const genres = Genre.find({}).exec()
3
    .then((genres) => {
4
      res.render('addBooks', { genres })
5
    })
6
    .catch((err) => {
7
      throw err
8
    })
9
})
10
11
router.post('/add', (req, res, next) => {
12
  req.checkBody('name', 'Name is required').notEmpty()
13
  req.checkBody('description', 'Description is required').notEmpty()
14
  req.checkBody('genre', 'Genre is required').notEmpty
15
16
  const errors = req.validationErrors()
17
18
  if (errors) {
19
    console.log(errors)
20
    res.render('addBooks', { book, errors })
21
  }
22
23
  const book = (new Book(req.body)).save()
24
    .then((data) => {
25
      res.redirect('/books')
26
    })
27
    .catch((errors) => {
28
      console.log('oops...')
29
    })
30
})

Di router pertama, anda menampilkan halaman /addBooks. Router ini dipanggil ketika permintaan dibuat pada lokasi /add. Sejak buku-buku ditambahkan seharusnya memiliki genre, anda ingin menampilkan genre yang sudah disimpan ke database.

1
  const genres = Genre.find({}).exec()
2
      .then((genres) => {

Kode di atas mencari semua genre dalam database anda dan mengembalikan mereka pada variabel genre. Dengan ini, anda akan dapat melakukan perulangan melaui genre-genre dan menampilkannya sebagai sebuah checkbox.

Router kedua menangani pengajuan form. Pertama, anda cek isi permintaannya untuk memastikan bahwa beberapa field tidak kosong. Disinilah middleware express-validator yang anda atur dalam app.js menjadi berguna. Jika ada kesalahan, halaman di-render ulang. Jika tidak ada, misalnya Buku baru disimpan dan pengguna akan diarahkan ke halaman /books.

Mari kita lanjutkan dan buat view untuk ini.

Buat sebuah file view baru bernama addBooks.pug. Perhatikan bahwa nama view sesuai dengan parameter pertama yang diberikan kepada res.render. Hal ini karena anda me-render sebuah template. Selama pengalihan, anda cukup melalui lokasi yang anda ingin arahkan ulang, seperti yang anda lakukan dengan res.redirect('/books').

Setelah menetapkan itu, berikut seperti apa seharusnya view akan terlihat.

1
#views/addBooks.pug
2
3
extends layout
4
5
block content
6
  .row
7
    .col-md-12
8
      h1 Add Book
9
      form(method="POST", action="/books/add")
10
        .form-group
11
          label.col-lg-2.control-label Name
12
          .col-lg-10
13
            input.form-control(type="text", name='name')
14
        .form-group
15
          label.col-lg-2.control-label Author
16
          .col-lg-10
17
            input.form-control(type="text", name='author')
18
        .form-group
19
          label.col-lg-2.control-label Book Description
20
          .col-lg-10
21
            textarea#textArea.form-control(rows='3', name='description')
22
        .form-group
23
          label.col-lg-2.control-label Genre
24
          .col-lg-10
25
            for genre in genres
26
              .checkbox
27
                input.checkbox(type='checkbox', name='genre', id=genre._id, value=genre._id, checked=genre.checked)
28
                label(for=genre._id) #{genre.name}
29
        .form-group
30
          .col-lg-10.col-lg-offset-2
31
            input.button.btn.btn-primary(type='submit', value='Submit')
32
      
33
      if errors
34
        ul
35
          for error in errors
36
            li!= error.msg

Hal yang penting untuk dicatat di sini adalah aksi form dan metode. Ketika tombol kirim diklik, anda membuat permintaan POST ke /books/add. Satu hal lagi—sekali lagi anda melakuan perulangan melalui koleksi genre yang dikembalikan dan menampilkannya masing-masing.

Route Tampil Buku dan View

Mari kita turunkan route untuk menangani permintaan yang dilakukan untuk setiap halaman buku. Sementara anda di sana, sangat penting untuk mengekspor modul anda juga.

1
#routes/books.js
2
3
router.get('/show/:id', (req, res, next) => {
4
  const book = Book.findById({ _id: req.params.id })
5
  .populate({
6
    path: 'genre',
7
    model: 'Genre',
8
    populate: {
9
      path: 'genre',
10
      model: 'Book'
11
    }
12
  })
13
  .exec()
14
    .then((book) => {
15
      res.render('book', { book })
16
    })
17
    .catch((err) => {
18
      throw err
19
    })
20
})
21
22
module.exports = router;

Tidak ada keajaiban yang terjadi sini

Pertama, permintaan yang dilakukan ke router ini harus memiliki id: yaitu id dari buku. Id ini diperoleh dari parameter permintaan menggunakan req.params.id. Ini digunakan untuk mengidentifikasi buku tertentu yang harus diperoleh dari database, sebagai id yang unik. Ketika buku ditemukan, nilai buku genre diisi dengan semua genre yang telah disimpan untuk contoh buku ini. Jika semua berjalan lancar, view buku akan di-render, jika tidak sebuah error akan muncul.

Mari kita buat view untuk sebuah buku. Berikut adalah bagaimana hal ini akan terlihat.

1
block content
2
  .well.well-lg
3
    h1 #[strong Name:] #{book.name}
4
    ul
5
      li #[strong Description:] #{book.description}
6
      li #[strong Author]: #{book.author}
7
      li #[strong Genre:]
8
        each genre in book.genre
9
          #{genre.name}
10
          |,

Anda dapat memuliai node server anda dengan menjalankan:

1
DEBUG=tutsplus-library:* npm start

Kesimpulan

Sekarang Anda tahu bagaimana membangun sebuah aplikasi web standar di Node.js, bukan hanya sebuah aplikasi agenda sederhana. Anda mampu untuk menangani pengiriman form, mereferensi dua model, dan mengatur beberapa middleware.

Anda dapat melangkah lebih jauh dengan mengembangkan aplikasinya—coba tambahkan kemampuan untuk menghapus sebuah buku. Pertama tambahkan tombol ke halaman tampil, dan kemudian pergi ke file route dan tambahkan router untuk ini. Perhatikan bahwa ini akan menjadi permintaan POST.

Anda juga bisa memikirkan lebih banyak fitur untuk menambahkannya pada aplikasi. Saya harap anda menikmatinya.