Membuat Aplikasi Realtime Location Menggunakan Node JS Dan Socket.IO - CRUDPRO

Membuat Aplikasi Realtime Location Menggunakan Node JS Dan Socket.IO

Membuat Aplikasi Realtime Location Menggunakan Node JS Dan Socket.IO

Socket.IO menyediakan komunikasi antara klien web dan server Node.js secara real time. Untuk banyak kasus penggunaan saat ini, pengembang perlu terus memperbarui informasi untuk aplikasi mereka secara real time, yang memerlukan penggunaan alat komunikasi dua arah yang dapat memperbarui data secara instan.

Dalam artikel ini, kita akan melihat bagaimana Anda dapat menggunakan Socket.IO dengan Node.js untuk komunikasi data real-time untuk aplikasi Anda untuk kasus penggunaan seperti ini.

REST API vs WebSockets

REST API tradisional paling berguna saat kita ingin mengambil sumber daya dan tidak memerlukan pembaruan berkelanjutan yang konstan.

Jika Anda melihat perdagangan kripto, misalnya, ketika kami mengajukan tawaran untuk membeli koin, tawaran tersebut kemungkinan besar tidak akan sering berubah, jadi kami dapat memodelkan perilaku ini secara akurat menggunakan REST API tradisional.

Namun, harga sebenarnya dari koin itu sendiri sangat fluktuatif, karena ia merespons tren pasar, seperti aset apa pun. Agar kami mendapatkan harga terbaru, kami perlu mengajukan permintaan dan kemungkinan besar itu akan berubah lagi begitu tanggapan kami tiba!

Dalam skenario seperti itu, kita perlu diberi tahu saat harga aset berubah, di situlah WebSockets bersinar.

Sumber daya yang digunakan untuk membangun REST API tradisional sangat dapat disimpan dalam cache karena jarang diperbarui. WebSockets, sementara itu, tidak mendapat banyak manfaat dari caching, karena ini mungkin memiliki efek negatif pada kinerja.

Ada panduan ekstensif yang menyoroti kasus penggunaan berbeda untuk REST API dan WebSockets tradisional.

Dalam panduan ini, kami sedang membangun aplikasi berbagi lokasi real-time menggunakan Node.js dan Socket.IO sebagai kasus penggunaan kami.

Berikut adalah teknologi yang akan kami gunakan:

  • Node.js/Express: Untuk server aplikasi kami
  • Socket.IO: Mengimplementasikan WebSockets di bawah tenda
  • Postgres:Database pilihan untuk menyimpan data lokasi pengguna
  • Postgis: Ekstensi ini memungkinkan untuk bekerja dengan lokasi di database dan menyediakan fungsi tambahan, seperti menghitung jarak di sekitar lokasi

Menyiapkan server Express.js

Kami akan mulai dengan menyiapkan aplikasi Express.

Untuk ini, saya telah memodifikasi boilerplate yang akan kita gunakan. Mari ikuti petunjuk ini untuk memulai:

  • Kloning repositori GitHub ini
  • cd socket-location/
  • npm install
  • Terakhir, buat file .env di root dan salin konten env. berkas sampel. Untuk mendapatkan nilainya, kita sebenarnya perlu menyiapkan database lokal atau menggunakan platform database Postgres online seperti Elephant atau Heroku. Kunjungi platform dan buat database gratis, dapatkan URL, isi kredensial, dan mulai aplikasi

Menyiapkan WebSockets dengan Socket.IO

Untuk menyiapkan Sockets.IO dengan file starter yang ada, penting bagi kami untuk menggunakan JavaScript Hoisting, yang memungkinkan kami membuat instance socket tersedia di berbagai file.

Kami akan mulai dengan menginstal Socket.IO

npm install socket.io

Hal berikutnya yang perlu kita lakukan adalah mengintegrasikannya dengan aplikasi ekspres kita. Buka file app.js di root direktori proyek dan edit dengan cara berikut:

const express = require("express");
const { createServer } = require("http");
const { Server } = require("socket.io");
const { initializeRoutes } = require("./routes");

let app = express();
const port = 3000;
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

app = initializeRoutes(app);
app.get("/", (req, res) => {
  res.status(200).send({
    success: true,
    message: "welcome to the beginning of greatness",
  });
});

const httpServer = createServer(app);
const io = new Server(httpServer, {
  cors: {
    origin: "*",
    methods: ["GET", "POST"],
  },
});

io.on("connection", (socket) => {
  console.log("We are live and connected");
  console.log(socket.id);
});

httpServer.listen(port, () => {
  console.log(`Example app listening on port ${port}`);
});

Kami telah berhasil menghubungkan server kami untuk bekerja dengan soket; sekarang kita perlu mengirim klien kita untuk menanggapi koneksi ini sehingga dapat terjadi komunikasi bolak-balik.

Klien kami dapat berupa aplikasi web, aplikasi seluler, atau perangkat apa pun yang dapat dikonfigurasi untuk bekerja dengan WebSockets.

Kami akan meniru perilaku klien dalam aplikasi ini dengan POSTMAN demi singkatnya dan kesederhanaan.

Untuk melakukannya, lakukan hal berikut:

  1. Buka Postman dan klik tombol "Baru" di sudut kiri atas
  2. Kemudian klik tombol "Permintaan Websocket" pada pop-up
  3. Seharusnya ada teks yang mengatakan "raw" - gunakan drop down dan ubah ke "Socket.IO"
  4. Pasang string koneksi Anda pada tab — dalam hal ini, localhost:3000 — dan klik tombol sambungkan

Anda akan melihat ID unik yang dicetak ke konsol dan tab Tukang Pos akan mengatakan "Terhubung".

Otentikasi dan otorisasi

Sekarang kami telah berhasil terhubung ke instance Socket.IO kami, kami perlu menyediakan beberapa bentuk otentikasi dan otorisasi untuk mencegah pengguna yang tidak diinginkan keluar. Ini terutama terjadi ketika Anda menganggap bahwa kami mengizinkan koneksi dari klien mana pun, seperti yang ditentukan oleh * dalam opsi CORS kami saat menghubungkan.

Untuk melakukan ini, kita akan menggunakan middleware ini:

io.use((socket, next) => {
  if (socket.handshake.headers.auth) {
    const { auth } = socket.handshake.headers;
    const token = auth.split(" ")[1];
    jwt.verify(token, process.env.JWT_SECRET_KEY, async (err, decodedToken) => {
      if (err) {
        throw new Error("Authentication error, Invalid Token supplied");
      }
      const theUser = await db.User.findByPk(decodedToken.id);
      if (!theUser)
        throw new Error(
          "Invalid Email or Password, Kindly contact the admin if this is an anomaly"
        );
      socket.theUser = theUser;
      return next();
    });
  } else {
    throw new Error("Authentication error, Please provide a token");
  }
});

Pertama, kami memeriksa apakah token disediakan di header permintaan. Kami juga memeriksa apakah itu token yang valid dan apakah pengguna ada di database kami sebelum mengizinkan mereka untuk terhubung — ini memastikan hanya pengguna yang diautentikasi yang memiliki akses.

Berbagi lokasi antar pengguna

Untuk berbagi lokasi antar pengguna, kami akan menggunakan koneksi soket. Kita juga perlu menyiapkan database kita untuk menerima objek geometri, dan untuk melakukan ini kita akan menginstal ekstensi PostGIS pada database kita. Di klien, kita akan menggunakan JavaScript Geolocation API.

Pertama, kita akan menginstal ekstensi PostGIS di database kita.

Jalankan perintah ini di root proyek:

 npx sequelize-cli migration:generate --name install-postgis

Kemudian, buka file migrasi yang dihasilkan dan rekatkan yang berikut ini:

"use strict";
/** @type {import('sequelize-cli').Migration} */
module.exports = {
  up: (queryInterface, Sequelize) => {
    return queryInterface.sequelize.query("CREATE EXTENSION postgis;");
  },
  down: (queryInterface, Sequelize) => {
    return queryInterface.sequelize.query("DROP EXTENSION postgis CASCADE;");
  },
};

Cuplikan kode di atas akan menginstal ekstensi postgis pada instance database kami ketika kami menjalankan migrasi.

npm run migrate
(Note: Pada saat penulisan, saya tidak berhasil menjalankan migrasi untuk menginstal ekstensi PostGIS pada instance Postgres yang disediakan oleh ElephantSQL karena saya memiliki masalah izin, tetapi saya berhasil menjalankan yang sama pada instance Heroku)

Selanjutnya, kita perlu menyiapkan database untuk menyimpan informasi lokasi pengguna, dan untuk melakukan ini, kita akan membuat file migrasi lain:

npx sequelize-cli model:generate --name Geolocation --attributes socketID:string,location:string

Hapus konten dan rekatkan yang berikut ini:

"use strict";

/** @type {import('sequelize-cli').Migration} */

module.exports = {
  async up(queryInterface, Sequelize) {
    await queryInterface.createTable("Geolocations", {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: Sequelize.INTEGER,
        references: {
          model: "Users",
          key: "id",
          as: "id",
        },
      },
      socketID: {
        type: Sequelize.STRING,
        unique: true,
      },
      location: {
        type: Sequelize.GEOMETRY,
      },
      online: {
        type: Sequelize.BOOLEAN,
      },
      trackerID: {
        type: Sequelize.INTEGER,
        references: {
          model: "Users",
          key: "id",
          as: "id",
        },
      },
      createdAt: {
        allowNull: false,
        type: Sequelize.DATE,
      },
      updatedAt: {
        allowNull: false,
        type: Sequelize.DATE,
      },
    });
  },
  async down(queryInterface, Sequelize) {
    await queryInterface.dropTable("Geolocations");
  },
};

dan kemudian kami menjalankan migrasi lagi:

npm run migrate

Kami juga ingin membuat hubungan antara dua model, jadi di models/user.js, tambahkan kode berikut. Di sini kita membuat hubungan satu-ke-satu antara model pengguna dan model Geolokasi.

...
static associate(models) {
      // define association here
      this.hasOne(models.Geolocation, { foreignKey: "id" });
    }
...

Kemudian, di models/geolocation.js, tambahkan:

.... 
static associate(models) {
      // define association here
      this.belongsTo(models.User, { foreignKey: "id" });
    }
....

Selanjutnya, kita perlu membangun route aktual yang memfasilitasi koneksi. Untuk melakukan ini, masuk ke routes dan buat /track/track.js. Kemudian, tambahkan yang berikut ini:

const { Router } = require("express");
const db = require("../../models");
const { handleJwt } = require("../../utils/handleJwt");
const trackerRouter = Router();
trackerRouter.post(
  "/book-ride",
  handleJwt.verifyToken,
  async (req, res, next) => {
    // search for user that is offline
    // assign the booker id to the
    const {
      user,
      body: { location },
    } = req;
    //returns the first user that meets the criteria
    const user2 = await db.User.findOne({
      where: { role: "driver" },
    });
    db.Geolocation.update(
      {
        trackerID: user2.id,
        online: true,
      },
      { where: { id: user.id }, returning: true }
    );
    db.Geolocation.update(
      {
        trackerID: user.id,
        location: {
          type: "Point",
          coordinates: [location.longitude, location.latitude],
        },
        online: true,
      },
      { where: { id: user2.id }, returning: true }
    );
    if (!user2)
      return res.status(404).send({
        success: false,
        message,
      });
    return res.status(200).send({
      success: true,
      message: "You have successfully been assigned a driver",
    });
  }
);
module.exports = { route: trackerRouter, name: "track" };

Selain itu, kami juga ingin memastikan bahwa setiap kali kami mendaftarkan pengguna, kami juga membuat objek geolokasi untuk mereka.

Tambahkan ini tepat sebelum Anda mengirim respons saat membuat pengguna di file route/user/user.js:

....
await db.Geolocation.create({ id: user.id });
const token = handleJwt.signToken(user.dataValues);
.....

jika Anda sekarang membuka POSTMAN dan mengirim permintaan, Anda akan mendapatkan respons bahwa Anda telah berhasil diberi driver.

Mengirim dan menerima acara

Kami akan mengirim dan menerima objek yang mewakili posisi geografis pengguna, yang akan kami lakukan di web melalui API Geolokasi.

Ini cukup sederhana untuk digunakan dan sangat akurat untuk sebagian besar perangkat konsumen modern seperti smartphone dan laptop, karena mereka memiliki GPS bawaan.

Saat kami keluar dari objek yang menangkap informasi ini, kami memiliki:

position: {
          coords: {
                       accuracy: 7299.612787273156
                       altitude: null
                       altitudeAccuracy: null
                       heading: null
                       latitude: 6.5568768
                       longitude: 3.3488896
                       speed: null
           },
        timestamp: 1665474267691
}

Jika Anda mengunjungi w3schools dengan ponsel Anda dan berjalan-jalan sambil melihat informasi bujur dan lintang, Anda akan menemukan bahwa lokasi Anda terus berubah untuk mencerminkan di mana Anda berada secara real time.

Ini karena detail lokasi baru Anda diaktifkan saat Anda bergerak! Ada informasi lain yang ditangkap dalam posisi objek, yang meliputi kecepatan serta ketinggian.

Sekarang, mari buat penangan soket yang benar-benar akan memancarkan dan mendengarkan peristiwa ini antara pengguna dan driver.

di app.js kami, edit baris berikut agar terlihat seperti ini:

const { onConnection } = require("./socket");

....
io.on("connection", onConnection(io));
....

Kemudian, kita akan membuat direktori socket di root aplikasi kita dan menambahkan file-file berikut:

driversocket.js

const { updateDbWithNewLocation } = require("./helpers");
const db = require("../models");
const hoistedIODriver = (io, socket) => {
  return async function driverLocation(payload) {
    console.log(`driver-move event has been received with ${payload} 🐥🥶`);
    const isOnline = await db.Geolocation.findByPk(payload.id);
    if (isOnline.dataValues.online) {
      const recipient = await updateDbWithNewLocation(payload, isOnline);
      if (recipient.trackerID) {
        const deliverTo = await db.Geolocation.findOne({
          where: { trackerID: recipient.trackerID },
        });
        const { socketID } = deliverTo.dataValues;
        io.to(socketID).emit("driver:move", {
          location: recipient.location,
        });
      }
    }
  };
};
module.exports = { hoistedIODriver };

Kode di atas menangani pembaruan database dengan lokasi driver dan menyiarkannya ke pengguna yang mendengarkan.

userocket.js

const { updateDbWithNewLocation } = require("./helpers");
const db = require("../models");
const hoistedIOUser = (io, socket) => {
  return async function driverLocation(payload) {
    console.log(
      `user-move event has been received with ${JSON.stringify(payload)} 🍅🍋`
    );
    const isOnline = await db.Geolocation.findByPk(payload.id);
    if (isOnline.dataValues.online) {
      const recipient = await updateDbWithNewLocation(payload, isOnline);
      if (recipient.trackerID) {
        const deliverTo = await db.Geolocation.findOne({
          where: { trackerID: recipient.trackerID },
        });
        const { socketID } = deliverTo.dataValues;
        io.to(socketID).emit("user:move", {
          location: recipient.location,
        });
      }
    }
  };
};
module.exports = { hoistedIOUser };

Kode di atas menangani pembaruan basis data dengan lokasi pengguna dan menyiarkannya ke driver yang mendengarkan.

index.js

const { hoistedIODriver } = require("./driversocket");
const { hoistedIOUser } = require("./usersocket");
const configureSockets = (io, socket) => {
  return {
    driverLocation: hoistedIODriver(io),
    userLocation: hoistedIOUser(io),
  };
};
const onConnection = (io) => (socket) => {
  const { userLocation, driverLocation } = configureSockets(io, socket);
  socket.on("user-move", userLocation);
  socket.on("driver-move", driverLocation);
};
module.exports = { onConnection };

… dan akhirnya:

helpers.js

const db = require("../models");
const updateDbWithNewLocation = async (payload, oldGeoLocationInfo) => {
  const { id, socketID } = payload;
  const [, [newLocation]] = await db.Geolocation.update(
    {
      online: oldGeoLocationInfo.online,
      socketID,
      trackerID: oldGeoLocationInfo.trackerID,
      location: {
        type: "Point",
        coordinates: [payload.coords.longitude, payload.coords.latitude],
      },
    },
    { where: { id }, returning: true }
  );
  return newLocation;
};
module.exports = { updateDbWithNewLocation };

Untuk mendapatkan contoh kode kerja, silakan kunjungi repositori GitHub dan klon dan navigasikan antar cabang.

Kesimpulan

Terima kasih telah mengikuti tutorial ini tentang penggunaan Node.js dan Socket.IO untuk membangun aplikasi lokasi waktu nyata.

Kami dapat mendemonstrasikan bagaimana dengan alat dasar tetapi umum kami dapat mulai menerapkan fungsi aplikasi pelacakan lokasi dengan WebSockets.