<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Amalia Hajarani</title>
    <description>The latest articles on DEV Community by Amalia Hajarani (@amalja0).</description>
    <link>https://dev.to/amalja0</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1214814%2F3aa42d16-7926-4ff3-86ed-b4c83540231a.jpeg</url>
      <title>DEV Community: Amalia Hajarani</title>
      <link>https://dev.to/amalja0</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/amalja0"/>
    <language>en</language>
    <item>
      <title>How to: Integrating Socket.IO with Spring Boot and React (React Implementation)</title>
      <dc:creator>Amalia Hajarani</dc:creator>
      <pubDate>Wed, 06 Dec 2023 03:28:20 +0000</pubDate>
      <link>https://dev.to/amalja0/how-to-integrating-socketio-with-spring-boot-and-react-react-implementation-43nj</link>
      <guid>https://dev.to/amalja0/how-to-integrating-socketio-with-spring-boot-and-react-react-implementation-43nj</guid>
      <description>&lt;h2&gt;
  
  
  Prerequisite
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Node version of 20.10.0&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Initializing React-Vite project
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Open a directory dedicated to save this project.&lt;/li&gt;
&lt;li&gt;Open command prompt from that directory. Run this command:
&lt;code&gt;
npm create vite@latest .
&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Choose &lt;code&gt;React&lt;/code&gt; by moving your arrow key in keyboard. Then hit &lt;code&gt;enter&lt;/code&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi4d7jde0lqzr0z9xqfh7.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Choose &lt;code&gt;JavaScript&lt;/code&gt; for a variant. Then hit &lt;code&gt;enter&lt;/code&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftpscrsps1wofr9rubzz0.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Now run &lt;code&gt;npm install&lt;/code&gt;.
## Installing dependencies
I have some preferred dependency to install to make development easier like css framework and such. Still in the command prompt directed to your project directory, run these commands:
### Installing Material UI
&lt;code&gt;
npm install @mui/material @emotion/react @emotion/styled
&lt;/code&gt;
### Installing Axios
&lt;code&gt;
npm install axios
&lt;/code&gt;
### Installing Socket.io-client
We are going to use specific version.
&lt;code&gt;
npm install socket.io-client@2.1.1
&lt;/code&gt;
### Installing Formik and Yup
&lt;code&gt;
npm install formik yup
&lt;/code&gt;
## Creating project skeleton
This is the overview of my project skeleton inside &lt;code&gt;src&lt;/code&gt; directory:
&lt;code&gt;
.
└── src/
├── assets/
├── components/
├── hooks/
├── service/
├── views/
├── app.jsx
├── index.css
├── main.jsx
└── theme.js
&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Defining theme
&lt;/h2&gt;

&lt;p&gt;This step is actually not necessary, but if you want to customize some things like typography and stuffs, you might want to follow this step. As you can see in my &lt;code&gt;src&lt;/code&gt; skeleton. I have a file called &lt;code&gt;theme.js&lt;/code&gt;. If you already create that, this is my content of the file:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { createTheme, responsiveFontSizes } from '@mui/material/styles';

let theme = createTheme({
  palette: {
    primary: {
      main: "#003c6d",
      contrastText: "#fff"
    },
    secondary: {
      main: "#B0B0B0",
    }
  },
  typography: {
    fontFamily: [
      'Roboto', 
      'sans-serif'
    ].join(',')
  },
  components: {
    MuiTextField: {
      styleOverrides: {
        root: {
          '&amp;amp; .MuiOutlinedInput-root': {
              borderRadius: 8,
          },
        }
      }
    },
    MuiButton: {
      styleOverrides: {
        root: {
          borderRadius: 8,
          paddingTop: 12,
          paddingBottom: 12
        }
      }
    }
  }
});

theme = responsiveFontSizes(theme);

export default theme;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;I'm using font from Google font, hence I need to update the &lt;code&gt;index.css&lt;/code&gt; like this (and this is also my final &lt;code&gt;index.css&lt;/code&gt;):&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&amp;amp;display=swap');

:root {
  font-family: 'Roboto', sans-serif;
}

::-webkit-scrollbar {
  display: none;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;To apply the theme that we've already created, open &lt;code&gt;App.jsx&lt;/code&gt;. This is how my final &lt;code&gt;App.jsx&lt;/code&gt; lookslike:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { ThemeProvider } from '@mui/material';
import { useState } from 'react';
import theme from './theme';
import ChatRoom from './views/ChatRoom';
import Login from './views/Login/indes';

function App() {
  const [username, setUsername] = useState("");
  const [room, setRoom] = useState("");
  const [isLoggedIn, setIsLoggedIn] = useState(false);

  return (
    &amp;lt;ThemeProvider theme={theme}&amp;gt;
      {
        !isLoggedIn ? (
          &amp;lt;Login
            room={room}
            setRoom={setRoom}
            username={username}
            setUsername={setUsername}
            setIsLoggedin={setIsLoggedIn}
          /&amp;gt;
        ) : (
          &amp;lt;ChatRoom 
            username={username}
            room={room}
          /&amp;gt;
        )
      }
    &amp;lt;/ThemeProvider&amp;gt;
  )
}

export default App

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If you see the coed above carefully, the &lt;code&gt;theme.js&lt;/code&gt; is used along with &lt;code&gt;&amp;lt;ThemeProvider theme={theme}&amp;gt; &amp;lt;/ThemeProvider&amp;gt;&lt;/code&gt; tag.&lt;/p&gt;

&lt;h2&gt;
  
  
  App.jsx explanation
&lt;/h2&gt;

&lt;p&gt;I'm not using routing in this project like the tutorial I've been mentioned in the first post. So, the logic is actually pretty simple. It uses an approach like lifting state up where &lt;code&gt;Login&lt;/code&gt; return the most updated state of &lt;code&gt;username&lt;/code&gt;, &lt;code&gt;room&lt;/code&gt; and &lt;code&gt;isLoggedIn&lt;/code&gt; to the parent (&lt;code&gt;App.jsx&lt;/code&gt;) to be then consumed by &lt;code&gt;ChatRoom&lt;/code&gt; component.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring .env file
&lt;/h2&gt;

&lt;p&gt;On your project root directory, create a new file called &lt;code&gt;.env&lt;/code&gt;. And fill it with your server-side address which by the way is your IPv4 address and the port where socket is running. Mine is at 8086.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;VITE_API_BASE_URL=http://{YOUR_IPv4_ADDRESS}:8080
VITE_SOCKET_BASE_URL=http://{YOUR_IPv4_ADDRESS}:{SOCKET_PORT}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Defining service
&lt;/h2&gt;

&lt;p&gt;To working with the messages, we will create a service to communicate with our server side endpoint. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In &lt;code&gt;service&lt;/code&gt; directory, create a new directory called &lt;code&gt;config&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a new file called &lt;code&gt;axiosConfig.js&lt;/code&gt; at &lt;code&gt;config&lt;/code&gt;.&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import axios from "axios";

const API_BASE_URL = import.meta.env.VITE_API_BASE_URL;

const api = axios.create({
    baseURL: API_BASE_URL,
});

export default api;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Back to &lt;code&gt;service&lt;/code&gt; directory, create a new directory called &lt;code&gt;socket&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a new file called &lt;code&gt;index.js&lt;/code&gt; at &lt;code&gt;socket&lt;/code&gt; directory.&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import api from "../config/axiosConfig";

export const getSocketResponse = async (room) =&amp;gt; {
  try {
    const res = await api.get('/message/' + room);
    return res.data;
  } catch (error) {
    console.log(error);
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Defining custom hook
&lt;/h2&gt;

&lt;p&gt;In &lt;code&gt;hooks&lt;/code&gt; directory, create a new file called &lt;code&gt;useSocket.js&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { useCallback, useEffect, useState } from "react";
import * as io from 'socket.io-client'

export const useSocket = (room, username) =&amp;gt; {
    const [socket, setSocket] = useState();
    const [socketResponse, setSocketResponse] = useState({
        room: "",
        message: "",
        username: "",
        messageType:"",
        createdAt: ""
    });
    const [isConnected, setConnected] = useState(false);

    const sendData = useCallback((payload) =&amp;gt; {
        socket.emit("send_message", {
            room: room,
            message: payload.message,
            username: username,
            messageType: "CLIENT"
        });
    }, [socket, room]);

    useEffect(() =&amp;gt; {
        const socketBaseUrl = import.meta.env.VITE_SOCKET_BASE_URL;
        const s = io(socketBaseUrl, {
            query: `username=${username}&amp;amp;room=${room}`
        });
        setSocket(s);
        s.on("connect", () =&amp;gt; {
            setConnected(true);
        });
        s.on("connect_error", (error) =&amp;gt; {
            console.error("SOCKET CONNECTION ERROR", error);
        });
        s.on("read_message", (res) =&amp;gt; {
            setSocketResponse({
                room: res.room,
                message: res.message,
                username: res.username,
                messageType: res.messageType,
                createdAt: res.createdAt
            });
        });

        return () =&amp;gt; {
            s.disconnect();
        };
    }, [room, username]);

    return { isConnected, socketResponse, sendData };
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Creating Views
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Creating Login view
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;code&gt;views&lt;/code&gt; directory, create a new directory called &lt;code&gt;Login&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Inside &lt;code&gt;Login&lt;/code&gt; directory, create a new file called &lt;code&gt;index.jsx&lt;/code&gt;. Here, we're using Formik and Yup to validate login form, make sure all field is filled before the user allowed to enter a chat room.&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Button, Container, Grid, TextField, Typography } from '@mui/material';
import { useFormik } from 'formik';
import React from 'react';
import * as Yup from 'yup';

const initialValues = {
  username: "",
  room: ""
};

const validationSchema = Yup.object().shape({
  username: Yup.string().required(),
  room: Yup.string().required()
});

function Login({ setRoom, setUsername, setIsLoggedin }) {

  const onLogin = () =&amp;gt; {
    setUsername(formik.values.username);
    setRoom(formik.values.room);
    setIsLoggedin(true);
  };

  const formik = useFormik({
    initialValues: initialValues,
    validationSchema: validationSchema,
    onSubmit: () =&amp;gt; {
      onLogin();
    },
  });

  return (
    &amp;lt;Container&amp;gt;
        &amp;lt;Grid 
          container 
          gap={5}
          flexDirection={'column'} 
          alignItems={"center"} 
          justifyContent={"center"} 
          height={'97vh'}
        &amp;gt;
          &amp;lt;Grid item sx={{ width: '60%' }}&amp;gt;
            &amp;lt;Typography variant='h3' fontWeight={"bold"} color={"primary"}&amp;gt;Hello!&amp;lt;/Typography&amp;gt;
            &amp;lt;Typography color={"secondary"}&amp;gt;Login with your username&amp;lt;/Typography&amp;gt;
          &amp;lt;/Grid&amp;gt;
          &amp;lt;Grid item sx={{ width: '60%' }}&amp;gt;
            &amp;lt;TextField 
              id="outlined-basic"
              name="username" 
              label="Username" 
              variant="outlined"
              value={formik.values.username}
              onChange={formik.handleChange}
              onBlur={formik.handleBlur}
              fullWidth
              error={formik.touched.username &amp;amp;&amp;amp; formik.errors.username}
              helperText={formik.touched.username &amp;amp;&amp;amp; formik.errors.username &amp;amp;&amp;amp; "Username cannot be empty"}
            /&amp;gt;
          &amp;lt;/Grid&amp;gt;
          &amp;lt;Grid item sx={{ width: '60%' }}&amp;gt;
            &amp;lt;TextField 
              id="outlined-basic" 
              name="room" 
              label="Room" 
              variant="outlined"
              value={formik.values.room}
              onChange={formik.handleChange}
              onBlur={formik.handleBlur}
              fullWidth
              error={formik.touched.room &amp;amp;&amp;amp; formik.errors.room}
              helperText={formik.touched.room &amp;amp;&amp;amp; formik.errors.room &amp;amp;&amp;amp; "Room cannot be empty"}
            /&amp;gt;
          &amp;lt;/Grid&amp;gt;
          &amp;lt;Grid item sx={{ width: '60%' }}&amp;gt;
            &amp;lt;Button
              fullWidth
              variant="contained"
              type='submit'
              onClick={formik.handleSubmit}
            &amp;gt;
              Login
            &amp;lt;/Button&amp;gt;
          &amp;lt;/Grid&amp;gt;
        &amp;lt;/Grid&amp;gt;
    &amp;lt;/Container&amp;gt;
  )
};

export default Login;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;
  
  
  Creating ChatRoom view
&lt;/h3&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Open &lt;code&gt;views&lt;/code&gt; directory, create a new directory called &lt;code&gt;ChatRoom&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Inside &lt;code&gt;ChatRoom&lt;/code&gt; directory, create a new file called &lt;code&gt;index.jsx&lt;/code&gt;. Below is the content of this view, but mind that we haven't create &lt;code&gt;ChatBubble&lt;/code&gt; component.&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Box, Button, Container, Grid, TextField, Typography } from '@mui/material';
import React, { useEffect, useState } from 'react';
import ChatBubble from '../../components/ChatBubble';
import { useSocket } from '../../hooks/useSocket';
import { getSocketResponse } from '../../service/socket';
import { connect } from 'formik';

function ChatRoom({ username, room }) {
  const { isConnected, socketResponse, sendData } = useSocket(room, username);
  const [messageInput, setMessageInput] = useState("");
  const [messageList, setMessageList] = useState([]);

  const addMessageToList = (val) =&amp;gt; {
    if (val.room === "") return;
    setMessageList([...messageList]);
    fetchMessage();
  }

  const sendMessage = (e) =&amp;gt; {
    e.preventDefault();
    if (messageInput !== "") {
      sendData({
        message: messageInput
      });
      addMessageToList({
        message: messageInput,
        username: username,
        createdAt: new Date(),
        messageType: "CLIENT"
      });
      setMessageInput("");
    }
  }

  const fetchMessage = () =&amp;gt; {
    getSocketResponse(room)
            .then((res) =&amp;gt; {
                setMessageList([...res]);
            }).catch((err) =&amp;gt; {
                console.log(err);
            });
  }

  useEffect(() =&amp;gt; {
    fetchMessage();
  }, []);

  useEffect(() =&amp;gt; {
    addMessageToList(socketResponse);
  }, [socketResponse]);

  return (
    &amp;lt;Container&amp;gt;
      &amp;lt;Grid
        container 
        gap={3}
        flexDirection={'column'} 
        alignItems={"center"} 
        justifyContent={"center"} 
        height={'97vh'}
      &amp;gt;
        &amp;lt;Grid item sx={{ width: '60%' }}&amp;gt;
          &amp;lt;Typography variant='h5'&amp;gt;
            Welcome to room &amp;lt;b&amp;gt;{room}&amp;lt;/b&amp;gt;, {username}.
          &amp;lt;/Typography&amp;gt;
        &amp;lt;/Grid&amp;gt;
        &amp;lt;Grid item sx={{ width: '60%', bgcolor: '#ccd8e2', paddingX: '2rem', borderRadius: 6 }}&amp;gt;
          &amp;lt;Box 
            className="chat-box"
            sx={{ 
              width: '100%',
              paddingY: '2rem',
              borderRadius: 4,
              height: '60vh',
              overflow: 'auto'
            }}
          &amp;gt;
            {
              messageList.map((message) =&amp;gt; {
                if (message.messageType === 'CLIENT') {
                  console.log(message);
                  return (
                    &amp;lt;ChatBubble
                      key={message.id} 
                      isSender={message.username === username}
                      username={message.username}
                      message={message.message}
                      time={"12:12"}
                    /&amp;gt;
                  )
                } 
              })
            }
          &amp;lt;/Box&amp;gt;
          &amp;lt;Grid 
            container 
            alignItems={"center"}
            width={"100%"} 
            sx={{
              paddingY: '0.5rem',
              borderTop: '2px solid #99b1c5',
            }}
          &amp;gt;
            &amp;lt;Grid item xs={11}&amp;gt;
              &amp;lt;TextField 
                variant="standard"
                placeholder='Type your message'
                value={messageInput}
                onChange={(e) =&amp;gt; setMessageInput(e.target.value)}
                fullWidth
                InputProps={{
                  disableUnderline: true,
                  sx: {
                    paddingX: '0.5rem'
                  }
                }}
              /&amp;gt;
            &amp;lt;/Grid&amp;gt;
            &amp;lt;Grid item xs={1}&amp;gt;
              &amp;lt;Button
                onClick={(e) =&amp;gt; sendMessage(e)}
              &amp;gt;
                Send
              &amp;lt;/Button&amp;gt;
            &amp;lt;/Grid&amp;gt;
          &amp;lt;/Grid&amp;gt;
        &amp;lt;/Grid&amp;gt;
      &amp;lt;/Grid&amp;gt;
    &amp;lt;/Container&amp;gt;
  )
}

export default ChatRoom;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;
  
  
  Creating ChatBubbleComponent
&lt;/h2&gt;

&lt;p&gt;Moving to &lt;code&gt;components&lt;/code&gt; directory, create a new directory called &lt;code&gt;ChatBubble&lt;/code&gt;. Inside the newly created directory, create a new file called &lt;code&gt;index.jsx&lt;/code&gt;.&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Avatar, Box, Grid, Typography } from '@mui/material'
import React from 'react'
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;function ChatBubble({ isSender, username, message="" }) {&lt;br&gt;
  const avatar = "&lt;a href="https://random.imagecdn.app/500/150" rel="noopener noreferrer"&gt;https://random.imagecdn.app/500/150&lt;/a&gt;";&lt;br&gt;
  const date = new Date();&lt;br&gt;
  const time = date.getHours() + ':' + date.getMinutes();&lt;br&gt;
  return (&lt;br&gt;
    &lt;br&gt;
      
        container &lt;br&gt;
        gap={2} &lt;br&gt;
        flexDirection={isSender ? "row-reverse" : "row"}&lt;br&gt;
        sx={{&lt;br&gt;
          width: '100%',&lt;br&gt;
          display: 'flex',&lt;br&gt;
          justifyContent: isSender? 'end' : "start"&lt;br&gt;
        }} &lt;br&gt;
      &amp;gt;&lt;br&gt;
        &lt;br&gt;
          &lt;br&gt;
        &lt;br&gt;
        &lt;br&gt;
          &lt;br&gt;
             {username} &lt;br&gt;
            
              sx={{ &lt;br&gt;
                marginBottom: '0.5rem',&lt;br&gt;
                paddingRight: isSender ? '0.5rem' : '2rem',&lt;br&gt;
                paddingLeft: isSender ? '2rem' : '0.5rem',&lt;br&gt;
                paddingY: '0.25rem',&lt;br&gt;
                color: isSender ? '#e6ecf0' : '#001e37',&lt;br&gt;
                bgcolor: isSender ? '#001e37' : '#e6ecf0',&lt;br&gt;
                borderRadius: '8px'&lt;br&gt;
              }}&amp;gt;&lt;br&gt;
               {message} &lt;br&gt;
               {time} &lt;br&gt;
            &lt;br&gt;
          &lt;br&gt;
        &lt;br&gt;
      &lt;br&gt;
    &lt;br&gt;
  )&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;export default ChatBubble;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
## Running the application and demo.
1. Make sure server side service is running.
2. Open command prompt from project root directory and run:
    ```
    npm run dev
    ```
3. You will see this log if it's running correctly:
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fr2gj9g1suvphcz1ku98.png)
4. Now open two windows of your selected browser. Go to the address given in command prompt.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2twtjwh7uni6dk1n2zdz.png)
5. Enter different usernames and same room at both windows. Then login.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u6jg2secm9n4m63dykp4.png)
6. Try to chat from one account and see what happened:
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fb17i89wrb1a6rofzhcz.png)
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/emafsw9snhsiowab5mud.png)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>react</category>
      <category>beginners</category>
      <category>tutorial</category>
      <category>socketio</category>
    </item>
    <item>
      <title>How to: Integrating Socket.IO with Spring Boot and React (Spring Boot Implementation)</title>
      <dc:creator>Amalia Hajarani</dc:creator>
      <pubDate>Wed, 06 Dec 2023 02:29:52 +0000</pubDate>
      <link>https://dev.to/amalja0/how-to-integrating-socketio-with-spring-boot-and-react-spring-boot-implementation-1ma4</link>
      <guid>https://dev.to/amalja0/how-to-integrating-socketio-with-spring-boot-and-react-spring-boot-implementation-1ma4</guid>
      <description>&lt;p&gt;Hallo, dear reader. This post is actually my attempt to following an existing tutorial that was made by Gürkan UÇAR which tutorial can be seen at &lt;a href="https://medium.com/folksdev/spring-boot-netty-socket-io-example-3f21fcc1147d" rel="noopener noreferrer"&gt;here&lt;/a&gt;. You can also find the working github &lt;a href="https://github.com/gurkanucar/socketio-simple-chat/tree/master" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And my final result from following the tutorial can be seen at my github &lt;a href="https://github.com/amalja0/simple-chat-socket-io" rel="noopener noreferrer"&gt;repository&lt;/a&gt;. In this post I would like to focus on how to build the server side service so let's start cooking!&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisite
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Java version of 17.&lt;/li&gt;
&lt;li&gt;Visual Studio Code extension for &lt;a href="https://code.visualstudio.com/docs/java/java-spring-boot#:~:text=The%20Spring%20Initializr%20extension%20allows,for%20vscode%2Dspring%2Dinitializr%20." rel="noopener noreferrer"&gt;Spring Initializr&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Initializing Java Spring Boor project with Maven
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Open your Visual Studio Code.&lt;/li&gt;
&lt;li&gt;To create new project, usually I hit &lt;code&gt;ctrl&lt;/code&gt; + &lt;code&gt;shift&lt;/code&gt; + &lt;code&gt;p&lt;/code&gt; altogether, choose &lt;code&gt;Spring Initializr: Create Maven Project&lt;/code&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjsf1hdoz0acm1fjis1sq.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;We're going to use Spring Boot Version of &lt;code&gt;3.2.0&lt;/code&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Femcimuzfv941zsqrd0qt.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;We choose &lt;code&gt;Java&lt;/code&gt; as the language.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F923mrcdai8q5wf3a0c3k.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Type your group id. Mine is &lt;code&gt;com.tujuhsembilan&lt;/code&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7xqkjddhz2bumpmuui45.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7xqkjddhz2bumpmuui45.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Type your artifact id. Mine is &lt;code&gt;socketio&lt;/code&gt;.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft9c8h4l1h1jo4nwllmmv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft9c8h4l1h1jo4nwllmmv.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Choose &lt;code&gt;Jar&lt;/code&gt; as packaging type.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4b7vrznsfu58f4aozgzo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4b7vrznsfu58f4aozgzo.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Choose Java Version of 17.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq9mckzv7277ylya8atpt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq9mckzv7277ylya8atpt.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;For the dependencies, you can seet at my POM.xml. If you can't find it, that's fine because you can add it manually later, once your project is generated.&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;
&amp;lt;project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"&amp;gt;
    &amp;lt;modelVersion&amp;gt;4.0.0&amp;lt;/modelVersion&amp;gt;
    &amp;lt;parent&amp;gt;
        &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
        &amp;lt;artifactId&amp;gt;spring-boot-starter-parent&amp;lt;/artifactId&amp;gt;
        &amp;lt;version&amp;gt;3.2.0&amp;lt;/version&amp;gt;
        &amp;lt;relativePath/&amp;gt; &amp;lt;!-- lookup parent from repository --&amp;gt;
    &amp;lt;/parent&amp;gt;
    &amp;lt;groupId&amp;gt;com.tujuhsembilan&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;socketio&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;0.0.1-SNAPSHOT&amp;lt;/version&amp;gt;
    &amp;lt;name&amp;gt;socketio&amp;lt;/name&amp;gt;
    &amp;lt;description&amp;gt;Demo project for Spring Boot&amp;lt;/description&amp;gt;
    &amp;lt;properties&amp;gt;
        &amp;lt;java.version&amp;gt;17&amp;lt;/java.version&amp;gt;
        &amp;lt;netty-socketio.version&amp;gt;1.7.17&amp;lt;/netty-socketio.version&amp;gt;
    &amp;lt;/properties&amp;gt;
    &amp;lt;dependencies&amp;gt;
        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;spring-boot-starter-data-jpa&amp;lt;/artifactId&amp;gt;
        &amp;lt;/dependency&amp;gt;

        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;com.h2database&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;h2&amp;lt;/artifactId&amp;gt;
            &amp;lt;scope&amp;gt;runtime&amp;lt;/scope&amp;gt;
        &amp;lt;/dependency&amp;gt;

        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;org.projectlombok&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;lombok&amp;lt;/artifactId&amp;gt;
            &amp;lt;optional&amp;gt;true&amp;lt;/optional&amp;gt;
        &amp;lt;/dependency&amp;gt;

        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;spring-boot-starter-test&amp;lt;/artifactId&amp;gt;
            &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;
        &amp;lt;/dependency&amp;gt;

        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;com.corundumstudio.socketio&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;netty-socketio&amp;lt;/artifactId&amp;gt;
            &amp;lt;version&amp;gt;${netty-socketio.version}&amp;lt;/version&amp;gt;
        &amp;lt;/dependency&amp;gt;

        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;spring-boot-starter-web&amp;lt;/artifactId&amp;gt;
        &amp;lt;/dependency&amp;gt;

        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;spring-boot-starter-data-rest&amp;lt;/artifactId&amp;gt;
        &amp;lt;/dependency&amp;gt;
    &amp;lt;/dependencies&amp;gt;

    &amp;lt;build&amp;gt;
        &amp;lt;plugins&amp;gt;
            &amp;lt;plugin&amp;gt;
                &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
                &amp;lt;artifactId&amp;gt;spring-boot-maven-plugin&amp;lt;/artifactId&amp;gt;
                &amp;lt;configuration&amp;gt;
                    &amp;lt;excludes&amp;gt;
                        &amp;lt;exclude&amp;gt;
                            &amp;lt;groupId&amp;gt;org.projectlombok&amp;lt;/groupId&amp;gt;
                            &amp;lt;artifactId&amp;gt;lombok&amp;lt;/artifactId&amp;gt;
                        &amp;lt;/exclude&amp;gt;
                    &amp;lt;/excludes&amp;gt;
                &amp;lt;/configuration&amp;gt;
            &amp;lt;/plugin&amp;gt;
        &amp;lt;/plugins&amp;gt;
    &amp;lt;/build&amp;gt;

&amp;lt;/project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;10. Choose where you want to save the project.

## Creating project skeleton
1. Open `src\main\java\com\{your_group_id}\{your_artifact_id}`. Mine is `src\main\java\com\tujuhsembilan\socketio`.
2. Now I would suggest you to create few new packages like this:
    ```


    src/
        └── main/
            └── java/
                └── com/
                    └── tujuhsembilan/
                        └── socketio/
                            ├── configuration/
                            ├── constants/
                            ├── controller/
                            ├── enums/
                            ├── model/
                            ├── repository/
                            ├── service/
                            └── socket/


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Configuring application.properties
&lt;/h2&gt;

&lt;p&gt;Usually, you can find &lt;code&gt;application.properties&lt;/code&gt; at &lt;code&gt;src\main\resources\application.properties&lt;/code&gt;. You can copy paste these lines to &lt;code&gt;application.properties&lt;/code&gt;. Don't forget to make sure that the port for socket is free to use and more importantly, change the host to your IPv4 address.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

socket-server.port=8086 // you can change this to port that is free to use
socket-server.host=191.167.1.28 // change this to your IPv4 address
spring.h2.console.enabled=true
spring.datasource.url=jdbc:h2:mem:database


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Creating configurations
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open configuration package. Create a new class called &lt;code&gt;SocketIOConfig.java&lt;/code&gt;. This is the content of the class:&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

package com.tujuhsembilan.socketio.configuration;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.corundumstudio.socketio.SocketIOServer;

@Configuration
public class SocketIOConfig {

    @Value("${socket-server.host}")
    private String host;

    @Value("${socket-server.port}")
    private Integer port;

    @Bean
    public SocketIOServer socketIOServer() {

        com.corundumstudio.socketio.Configuration config = new com.corundumstudio.socketio.Configuration();
        config.setHostname(host);
        config.setPort(port);

        return new SocketIOServer(config);
    }

}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2. Still in the same package, create another new class called `ServerCommandLineRunner.java`. This is the content:
    ```


    package com.tujuhsembilan.socketio.configuration;

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.CommandLineRunner;
    import org.springframework.stereotype.Component;

    import com.corundumstudio.socketio.SocketIOServer;

    import lombok.RequiredArgsConstructor;

    @Component
    @RequiredArgsConstructor(onConstructor = @__(@Autowired))
    public class ServerCommandLineRunner implements CommandLineRunner {

        private final SocketIOServer server;

        @Override
        public void run(String... args) throws Exception {
            server.start();
        }
    }


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Creating model
&lt;/h2&gt;

&lt;p&gt;We only need a model called &lt;code&gt;Message&lt;/code&gt;. Open your &lt;code&gt;model&lt;/code&gt; package. Create a new file called &lt;code&gt;Message.java&lt;/code&gt;. This is the content:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

package com.tujuhsembilan.socketio.model;

import java.time.LocalDateTime;
import java.util.UUID;

import com.tujuhsembilan.socketio.enums.MessageType;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Builder
@Getter
@Setter
@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Message {

    @Id
    @GeneratedValue(strategy = GenerationType.UUID)
    private UUID id;

    @Column 
    @Enumerated(EnumType.STRING)
    private MessageType messageType;

    @Column
    private String room;

    @Column
    private String username;

    @Column
    private String message;

    @Column
    private LocalDateTime createdAt;

}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Creating enums
&lt;/h2&gt;

&lt;p&gt;As you might notice, we not yet have enums for &lt;code&gt;messageType&lt;/code&gt; in the model. Hence, we need to create one. Open &lt;code&gt;enums&lt;/code&gt; package, create a new class called &lt;code&gt;MessageType.java&lt;/code&gt; and copy paste this:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

package com.tujuhsembilan.socketio.enums;

public enum MessageType {
    SERVER, CLIENT
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now the &lt;code&gt;Message&lt;/code&gt; model should be working fine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating constants
&lt;/h2&gt;

&lt;p&gt;Now, before moving further, let's create some constants. Open &lt;code&gt;constants&lt;/code&gt; package. Create a new file called &lt;code&gt;Constants.js&lt;/code&gt; and below is the content:&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&lt;p&gt;package com.tujuhsembilan.socketio.constants;&lt;/p&gt;

&lt;p&gt;public class Constants {&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public static final String WELCOME_MESSAGE = "%s joined to chat";
public static final String DISCONNECT_MESSAGE = "%s disconnected";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;}&lt;/p&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Creating repository&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;In repository package, create a new repository class called &lt;code&gt;MessageRepository.java&lt;/code&gt;. This is the content for it:&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&lt;p&gt;package com.tujuhsembilan.socketio.repository;&lt;/p&gt;

&lt;p&gt;import java.util.List;&lt;br&gt;
import java.util.UUID;&lt;/p&gt;

&lt;p&gt;import org.springframework.data.jpa.repository.JpaRepository;&lt;br&gt;
import org.springframework.stereotype.Repository;&lt;/p&gt;

&lt;p&gt;import com.tujuhsembilan.socketio.model.Message;&lt;/p&gt;

&lt;p&gt;@Repository&lt;br&gt;
public interface MessageRepository extends JpaRepository&amp;lt;Message, UUID&amp;gt; {&lt;br&gt;
    List&amp;lt;Message&amp;gt; findAllByRoom(String room);&lt;br&gt;
}&lt;/p&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Creating services&lt;br&gt;
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;In service package, create a new class called &lt;code&gt;MessageService.java&lt;/code&gt;.&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

package com.tujuhsembilan.socketio.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.tujuhsembilan.socketio.model.Message;
import com.tujuhsembilan.socketio.repository.MessageRepository;

import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class MessageService {

    private final MessageRepository messageRepository;

    public List&amp;lt;Message&amp;gt; getMessage(String room) {
        return messageRepository.findAllByRoom(room);
    }

    public Message saveMessage(Message message) {
        return messageRepository.save(message);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2. Create another class in the same package called &lt;code&gt;SocketService.java&lt;/code&gt;.&lt;br&gt;
    ```
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.tujuhsembilan.socketio.service;

import org.springframework.stereotype.Service;

import com.corundumstudio.socketio.SocketIOClient;
import com.tujuhsembilan.socketio.enums.MessageType;
import com.tujuhsembilan.socketio.model.Message;

import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
public class SocketService {

    private final MessageService messageService;

    public void sendSocketmessage(SocketIOClient senderClient, Message message, String room) {
        for (
            SocketIOClient client: senderClient.getNamespace().getRoomOperations(room).getClients()
        ) {
            if (!client.getSessionId().equals(senderClient.getSessionId())) {
                client.sendEvent("read_message", message);
            }
        }
    }

    public void saveMessage(SocketIOClient senderClient, Message message) {

        Message storedMessage = messageService.saveMessage(
            Message.builder()
                .messageType(MessageType.CLIENT)
                .message(message.getMessage())
                .room(message.getRoom())
                .username(message.getUsername())
                .build()
        );

        sendSocketmessage(senderClient, storedMessage, message.getRoom());

    }

    public void saveInfoMessage(SocketIOClient senderClient, String message, String room) {
        Message storedMessage = messageService.saveMessage(
            Message.builder()
                .messageType(MessageType.SERVER)
                .message(message)
                .room(room)
                .build()
        );

        sendSocketmessage(senderClient, storedMessage, room);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Creating socket module&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;Now, we are going to create a socket module. Open &lt;code&gt;socket&lt;/code&gt; package, create a new file called &lt;code&gt;SocketModule.java&lt;/code&gt; like this:&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&lt;p&gt;package com.tujuhsembilan.socketio.socket;&lt;/p&gt;

&lt;p&gt;import java.util.stream.Collectors;&lt;/p&gt;

&lt;p&gt;import org.springframework.stereotype.Component;&lt;/p&gt;

&lt;p&gt;import com.corundumstudio.socketio.SocketIOServer;&lt;br&gt;
import com.corundumstudio.socketio.listener.ConnectListener;&lt;br&gt;
import com.corundumstudio.socketio.listener.DataListener;&lt;br&gt;
import com.corundumstudio.socketio.listener.DisconnectListener;&lt;br&gt;
import com.tujuhsembilan.socketio.constants.Constants;&lt;br&gt;
import com.tujuhsembilan.socketio.model.Message;&lt;br&gt;
import com.tujuhsembilan.socketio.service.SocketService;&lt;/p&gt;

&lt;p&gt;import lombok.extern.slf4j.Slf4j;&lt;/p&gt;

&lt;p&gt;@Slf4j&lt;br&gt;
@Component&lt;br&gt;
public class SocketModule {&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private final SocketIOServer server;

private final SocketService socketService;

public SocketModule(SocketIOServer server, SocketService socketService) {
    this.server = server;
    this.socketService = socketService;
    server.addConnectListener(this.onConnected());
    server.addDisconnectListener(this.onDisconnected());
    server.addEventListener("send_message", Message.class, this.onChatReceived());
}

private DataListener&amp;amp;lt;Message&amp;amp;gt; onChatReceived() {
    return (senderClient, data, ackSender) -&amp;amp;gt; {
        log.info(data.toString());
        socketService.saveMessage(senderClient, data);
    };
}

private ConnectListener onConnected() {
    return (client) -&amp;amp;gt; {
        var params = client.getHandshakeData().getUrlParams();
        String room = params.get("room").stream().collect(Collectors.joining());
        String username = params.get("username").stream().collect(Collectors.joining());
        client.joinRoom(room);
        socketService.saveInfoMessage(client, String.format(Constants.WELCOME_MESSAGE, username), room);
        log.info("Socket ID[{}] - room[{}] - username [{}]  Connected to chat module through", client.getSessionId().toString(), room, username);
    };

}

private DisconnectListener onDisconnected() {
    return client -&amp;amp;gt; {
        var params = client.getHandshakeData().getUrlParams();
        String room = params.get("room").stream().collect(Collectors.joining());
        String username = params.get("username").stream().collect(Collectors.joining());
        socketService.saveInfoMessage(client, String.format(Constants.DISCONNECT_MESSAGE, username), room);
        log.info("Socket ID[{}] - room[{}] - username [{}]  discnnected to chat module through", client.getSessionId().toString(), room, username);
    };
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;}&lt;/p&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Creating controller&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;Now, to get the message in a certain room, let's create a controller. Open &lt;code&gt;controller&lt;/code&gt; package, create a new file called &lt;code&gt;MessageController.java&lt;/code&gt;. This is the content for our controller:&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&lt;p&gt;package com.tujuhsembilan.socketio.controller;&lt;/p&gt;

&lt;p&gt;import java.util.List;&lt;/p&gt;

&lt;p&gt;import org.springframework.beans.factory.annotation.Autowired;&lt;br&gt;
import org.springframework.http.ResponseEntity;&lt;br&gt;
import org.springframework.web.bind.annotation.CrossOrigin;&lt;br&gt;
import org.springframework.web.bind.annotation.GetMapping;&lt;br&gt;
import org.springframework.web.bind.annotation.PathVariable;&lt;br&gt;
import org.springframework.web.bind.annotation.RequestMapping;&lt;br&gt;
import org.springframework.web.bind.annotation.RestController;&lt;/p&gt;

&lt;p&gt;import com.tujuhsembilan.socketio.model.Message;&lt;br&gt;
import com.tujuhsembilan.socketio.service.MessageService;&lt;/p&gt;

&lt;p&gt;import lombok.RequiredArgsConstructor;&lt;/p&gt;

&lt;p&gt;@RestController&lt;br&gt;
@RequestMapping("/message")&lt;br&gt;
@RequiredArgsConstructor(onConstructor = @__(@Autowired))&lt;br&gt;
public class MessageController {&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private final MessageService messageService;

@CrossOrigin
@GetMapping("/{room}")
public ResponseEntity&amp;amp;lt;List&amp;amp;lt;Message&amp;amp;gt;&amp;amp;gt; getMessages(@PathVariable String room) {
    return ResponseEntity.ok(messageService.getMessage(room));
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;}&lt;/p&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Running the application&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;Since I have extension for Spring Boot in my Visual Studio code, I just went into SocketioApplication.java which located in &lt;code&gt;src\main\java\com\tujuhsembilan\socketio\SocketioApplication.java&lt;/code&gt; and click &lt;code&gt;Run&lt;/code&gt;. If the applicaion running well, this is the kind of log that you will see:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqtmdo7a895egvcf7ttpw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqtmdo7a895egvcf7ttpw.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>socketio</category>
      <category>springboot</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to: Integrating Firebase Cloud Messaging - Angular - ExpressJs (2: Create and Integrate Angular with Firebase)</title>
      <dc:creator>Amalia Hajarani</dc:creator>
      <pubDate>Fri, 01 Dec 2023 22:20:23 +0000</pubDate>
      <link>https://dev.to/amalja0/how-to-integrating-firebase-cloud-messaging-angular-expressjs-2-create-and-integrate-angular-with-firebase-2f1e</link>
      <guid>https://dev.to/amalja0/how-to-integrating-firebase-cloud-messaging-angular-expressjs-2-create-and-integrate-angular-with-firebase-2f1e</guid>
      <description>&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Installed Node version of: v20.10.0.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing Angular Cli
&lt;/h2&gt;

&lt;p&gt;You can see the &lt;a href="https://angular.io/guide/setup-local" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt; for a better explanation.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open command prompt and run command below:
&lt;code&gt;
npm install -g @angular/cli
&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Create new Angular project
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Open a command prompt from a dedicated directory to store your Angular project.&lt;/li&gt;
&lt;li&gt;Run this command:
&lt;code&gt;
ng new angular-with-firebase // you can change angular-with-firebase with anything else
&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;You will be asked to configuring few things. For stysheet format, I choose CSS. Then hit enter.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhhijerlaqfwoxn5tsi2b.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;I don't know much about this so I choose no by typing &lt;code&gt;N&lt;/code&gt;, then hit enter.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc6tjri2xilh9bfbszbxi.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Your project now is being created.&lt;/li&gt;
&lt;li&gt;Once it done, open command prompt from inside your project and run &lt;code&gt;ng serve&lt;/code&gt; to see our new project. By default, we will have it run at &lt;code&gt;http://localhost:4200/&lt;/code&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F698qbylszl2rwayb8ksg.png" alt="Image description"&gt;
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fomaekk5v0e3582rjycpx.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Connecting Firebase with Angular
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Configuring Firebase environment
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Open your project dashboard on Firebase Console.&lt;/li&gt;
&lt;li&gt;Click at settings icon near the &lt;code&gt;Project Overview&lt;/code&gt; that you can find at upper left corner.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F21qm0b2v0a26k51wydll.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Choose &lt;code&gt;Project settings&lt;/code&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffeq9a8vys205x2gj7dua.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Scroll down to your apps section. You will see a code snippet, the same one as when we first create the project. I choose &lt;code&gt;npm&lt;/code&gt; because I will be using &lt;code&gt;npm&lt;/code&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fycsirb99bbvs7g02ag25.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Back to our Angular project directory, open command prompt. Let's install Firebase by running:
&lt;code&gt;
npm install firebase
&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Once Firebase package installed, open our project in a code editor. I'm using Visual Studio Code.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In &lt;code&gt;src&lt;/code&gt; directory, create new directory called &lt;code&gt;environments&lt;/code&gt;. Inside this new directory, create a new file called &lt;code&gt;environment.ts&lt;/code&gt;. This is what is going to be inside that file:&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const environment = {
  production: false,
  firebaseConfig: {

    // to cut the chase, just copy it the snippet on step 3
    apiKey: "abcdefghijklmnopqrstuvwxyz",
    authDomain: "domain-firebase.firebaseapp.com",
    projectId: "project-id",
    storageBucket: "storage-bucket",
    messagingSenderId: "1234567890",
    appId: "acnqw3nry23ibcadmqomxue38e23e28x29m",
    measurementId: "G-6GG2Q2LX8S",

    // You are not yet have this
    vapidKey: ""
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;To have your &lt;code&gt;vapidKey&lt;/code&gt;, go back to your Firebase Project settings.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Go to &lt;code&gt;Cloud Messaging&lt;/code&gt; tab.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmhavioiszglln6uhwa41.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmhavioiszglln6uhwa41.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Scroll until you see &lt;code&gt;Web configuration&lt;/code&gt; section. Click &lt;code&gt;Generate key pair&lt;/code&gt; button.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Forxn9l5fxc4b0c8n01o1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Forxn9l5fxc4b0c8n01o1.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Copy the value under &lt;code&gt;Key pair&lt;/code&gt; section.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe50dhfesap8txttwgflp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe50dhfesap8txttwgflp.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Let's get back to step &lt;strong&gt;7&lt;/strong&gt;. Paste your key to &lt;code&gt;vapidKey&lt;/code&gt; field.&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const environment = {
  production: false,
  firebaseConfig: {
    apiKey: "abcdefghijklmnopqrstuvwxyz",
    authDomain: "domain-firebase.firebaseapp.com",
    projectId: "project-id",
    storageBucket: "storage-bucket",
    messagingSenderId: "1234567890",
    appId: "acnqw3nry23ibcadmqomxue38e23e28x29m",
    measurementId: "G-6GG2Q2LX8S",
    vapidKey: "BIddLYYlBZA1mijjbsG5ixKrvNgCqmy2H-qgI-PXSn84danwe7dtJOXjs9IDhNo_nQK0QzRKA96ocW8SDbKk5nRU"
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now, back to &lt;code&gt;src&lt;/code&gt; directory, create new directory called &lt;code&gt;configs&lt;/code&gt;. Inside that directory, create a new file called &lt;code&gt;firebase.config.ts&lt;/code&gt;. This is the content of the newly created file:&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import firebase from 'firebase/compat/app';
import { environment } from "../environments/environment";
import 'firebase/compat/messaging';

firebase.initializeApp(environment.firebaseConfig);
export const messaging = firebase.messaging();
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Working with service workers
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;To work with service worker we going to need new dependency, so open a command prompt from your project directory and run:
&lt;code&gt;
npm install @angular/service-worker
&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Go to Firebase Project Settings.&lt;/li&gt;
&lt;li&gt;Go to Firebase Cloud Messaging API (V1), there you will see a table containing your Sender ID. Copy the Sender ID.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk3kqbena4xj9grte2ps3.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Back to our code editor, in &lt;code&gt;src&lt;/code&gt; directory, create a file called &lt;code&gt;manifest.json&lt;/code&gt;. Paste your Sender ID here.
&lt;code&gt;
{
  "gcm_sender_id": "YOUR_SENDER_ID"
}
&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Open &lt;code&gt;index.html&lt;/code&gt; at the same directory, in head tag, add:
&lt;code&gt;
&amp;lt;link rel="manifest" href="./manifest.json"&amp;gt;
&lt;/code&gt;
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F64pfbldevk66p0fm4slx.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Still in &lt;code&gt;src&lt;/code&gt; directory, create a file called &lt;code&gt;firebase-messaging-sw.js&lt;/code&gt;.&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;importScripts("https://www.gstatic.com/firebasejs/9.1.3/firebase-app-compat.js");
importScripts("https://www.gstatic.com/firebasejs/9.1.3/firebase-messaging-compat.js");

const firebaseConfig = {
  // to cut the chase, just copy it from your Firebase Project settings

    apiKey: "abcdefghijklmnopqrstuvwxyz",
    authDomain: "domain-firebase.firebaseapp.com",
    projectId: "project-id",
    storageBucket: "storage-bucket",
    messagingSenderId: "1234567890",
    appId: "acnqw3nry23ibcadmqomxue38e23e28x29m",
    measurementId: "G-6GG2Q2LX8S",
};

const app = firebase.initializeApp(firebaseConfig);
const messaging = firebase.messaging();
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Go to project root directory, and find &lt;code&gt;angular.json&lt;/code&gt;. Add this line in &lt;code&gt;assets&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"src/manifest.json",
 "src/firebase-messaging-sw.js"
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3zrpdl8w4aywxvtpcz06.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3zrpdl8w4aywxvtpcz06.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Back to command prompt, run:&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng build
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Creating new Angular Component
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Open command prompt from your Angular project directory. Run this command:
&lt;code&gt;
ng generate component notification --inline-template
&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Once it's done, you will see new directory inside &lt;code&gt;src/app&lt;/code&gt; called &lt;code&gt;notification&lt;/code&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7oyj0mmtofej87blk61c.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Now, with your code editor, open &lt;code&gt;notification.component.ts&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now, let's cook something from here. To check if we already correctly configure anything, I add some simple function. Now, my &lt;code&gt;notification.component.ts&lt;/code&gt; looks like:&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Component, OnInit } from '@angular/core';
import { messaging } from '../../configs/firebase.config';
import { environment } from '../../environments/environment';

@Component({
  selector: 'app-notification',
  standalone: true,
  imports: [],
  template: `
    &amp;lt;p&amp;gt;
      notification works!
    &amp;lt;/p&amp;gt;
  `,
  styleUrl: './notification.component.css'
})
export class NotificationComponent implements OnInit {

  ngOnInit(): void {
    this.requestPermission();
    this.listen();
  }

  requestPermission() {
    messaging.getToken({vapidKey: environment.firebaseConfig.vapidKey})
      .then((currentToken) =&amp;gt; {
        if (currentToken) {
          console.log(currentToken);
        } else {
          console.log('No registration token available. Request permission to generate one.');
        }
      }).catch((err) =&amp;gt; {
        console.log(err);
      });
  }

  listen() {
    messaging.onMessage((incomingMessage) =&amp;gt; {
      console.log(incomingMessage);
    })
  }

}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now, open &lt;code&gt;app.component.ts&lt;/code&gt; which you can find at &lt;code&gt;src\app\app.config.ts&lt;/code&gt;. Add &lt;code&gt;NotificationComponent&lt;/code&gt; at &lt;code&gt;imports&lt;/code&gt; section.&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterOutlet } from '@angular/router';
import { NotificationComponent } from './notification/notification.component';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [CommonModule, RouterOutlet, NotificationComponent],
  templateUrl: './app.component.html',
  styleUrl: './app.component.css'
})
export class AppComponent {
  title = 'angular-with-firebase';
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Open &lt;code&gt;app.component.html&lt;/code&gt; at &lt;code&gt;src\app\app.component.html&lt;/code&gt;. At the end of last &lt;code&gt;div&lt;/code&gt; close tag, add &lt;code&gt;&amp;lt;app-notification&amp;gt;&amp;lt;/app-notification&amp;gt;&lt;/code&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyvovallms117zm5dts88.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyvovallms117zm5dts88.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a new file called &lt;code&gt;app.module.ts&lt;/code&gt; at the same level directory as &lt;code&gt;app.component.ts&lt;/code&gt; and &lt;code&gt;app.component.html&lt;/code&gt; (here's the relative path: &lt;code&gt;src\app\app.module.ts&lt;/code&gt;). Here's how I defined it:&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { NgModule } from "@angular/core";
import { BrowserModule } from "@angular/platform-browser";
import { NotificationComponent } from "./notification/notification.component";
import { messaging } from "../configs/firebase.config";

@NgModule({
  declarations: [],
  imports: [
    BrowserModule,
    NotificationComponent
  ],
  providers: [
    { provide: 'messaging', useValue: messaging}
  ],
  bootstrap: []
})
export class AppModule {}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now let's try running our application. In your project command prompt, run:&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng serve
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Open our application at &lt;code&gt;http://localhost:4200/&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You will be asked for notification permission. Allow it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Now open your browser console by inspecting element. You will see your token in there. It means we already configured our application!&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftiv918x8cihx8svdlrlx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftiv918x8cihx8svdlrlx.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Checking push notification functionality
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Make sure you are still running your Angular project.&lt;/li&gt;
&lt;li&gt;Go to Firebase Project Settings.&lt;/li&gt;
&lt;li&gt;Go to &lt;code&gt;Cloud Messaging&lt;/code&gt; tab.&lt;/li&gt;
&lt;li&gt;You will see this section.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F27vns0msrq917ssi0lcm.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;In Cloud Messaging API (Legacy) which still disabled, click the kebab menu (the three dots).
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffhdnlpyvh6e9zngh55gh.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Choose &lt;code&gt;Manage API in Google Cloud Console&lt;/code&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl8wz41m8hazzxaphujn8.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;You will be redirected to a new tab. Click &lt;code&gt;Enable&lt;/code&gt; button.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvgcpp4kjnkd1o2773cp0.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Back to Firebase Project settings tab. Refresh it. You will see a new generated Server key by default.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsdzdre99qvdcgkgtgsb6.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Now open Postman. Create new &lt;code&gt;POST&lt;/code&gt; request. The endpoint is &lt;code&gt;https://fcm.googleapis.com/fcm/send&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Go to Headers tab.&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;Authorization&lt;/code&gt; as key and the value is &lt;code&gt;key=your_server_key&lt;/code&gt;. Server key is something that you got from step &lt;strong&gt;8&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Add another headers of &lt;code&gt;Content-Type&lt;/code&gt; and choose &lt;code&gt;application/json&lt;/code&gt;
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftx5f7ghhe2b81l4o0dus.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Go to Body tab. Choose &lt;code&gt;Raw&lt;/code&gt; and &lt;code&gt;JSON&lt;/code&gt; then copy this:
&lt;code&gt;
{
    "notification": {
        "title": "New Notification",
        "body": "This is a sample push notification."
    },
    "to": "insert token from Angular project console"
}
&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;code&gt;Send&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;In Postman, you will see this kind of response:
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkc7mpq9r9wz5mfa58jbi.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;In your Angular project console, you will see a notification coming in at the console:
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdoezh29ls4c1p2w9ce4j.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>angular</category>
      <category>typescript</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How to: Integrating Firebase Cloud Messaging - Angular - ExpressJs (Create Firebase App)</title>
      <dc:creator>Amalia Hajarani</dc:creator>
      <pubDate>Fri, 01 Dec 2023 06:37:43 +0000</pubDate>
      <link>https://dev.to/amalja0/how-to-integrating-firebase-cloud-messaging-angular-expressjs-2m5a</link>
      <guid>https://dev.to/amalja0/how-to-integrating-firebase-cloud-messaging-angular-expressjs-2m5a</guid>
      <description>&lt;p&gt;Hallo, dear reader! I'm coming back today! So, in this series, which is a short one, I will going to show you on how to create a very simple web app with push notification functionality using Firebase. I will tell you how do I do it step by step since it is my first time using Angular and Firebase.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating Firebase project
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Disclaimer&lt;/strong&gt;: I've already had an account so I can't really tell you how to make one because somehow I just have it.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open firebase console. If you already had an account set up, I think you can just click &lt;a href="https://console.firebase.google.com/u/0/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Add new project.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0j991a9afxb0ou8rcl5b.png" alt="Image description"&gt;
3.Give your project a name. Then click &lt;code&gt;Continue&lt;/code&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzx9mtqo519c7oggihgfl.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;I keep the Google Analytics enabled so I just click &lt;code&gt;Continue&lt;/code&gt;
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg78z62yjd2afrpqlbvqu.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Select an account. I choose &lt;code&gt;Default Account for Firebase&lt;/code&gt;. Then click &lt;code&gt;Create project&lt;/code&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqtayhahdt09jajdqvp4b.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Wait until your project is ready. Then click &lt;code&gt;Continue&lt;/code&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feovswqpi30zlzm2v4ujp.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Now you're being redirected to your project dashboard.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsy1hq7edbnq03irgjfd3.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Creating a web-app from Firebase
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;From your dashboard project, choose &lt;code&gt;&amp;lt;/&amp;gt;&lt;/code&gt; icon.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faza20kbyixkugpmosjj3.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Give your web app some nick name. I don't tick the set up Firebase Hosting. Then click &lt;code&gt;Register app&lt;/code&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6lv7s4s676a2ttut0um7.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Then you will see some code snippet like this below. You don't have to worry about loosing it because we can see it again later.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcb5dz90he5zul09c8lot.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Now, let's just click &lt;code&gt;Continue to console&lt;/code&gt; at the bottom of the same page.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Please do check the next post on how to connect your Firebase with Angular because I'll be taking you from the very beginning on how to create an Angular App.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>angular</category>
      <category>firebase</category>
      <category>express</category>
    </item>
    <item>
      <title>Customer: Use Case (5: Implementing Simple Jenkins Continuous Integration)</title>
      <dc:creator>Amalia Hajarani</dc:creator>
      <pubDate>Thu, 30 Nov 2023 03:48:24 +0000</pubDate>
      <link>https://dev.to/amalja0/customer-use-case-5-implementing-simple-jenkins-continuous-integration-9ig</link>
      <guid>https://dev.to/amalja0/customer-use-case-5-implementing-simple-jenkins-continuous-integration-9ig</guid>
      <description>&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Installed Node version of: v20.10.0.&lt;/li&gt;
&lt;li&gt;Installed and configured Jenkins. If you want to know how, you can check my post on how to do it in &lt;a href="https://dev.to/amalja0/how-to-sonarqube-code-coverage-in-jenkins-with-maven-project-3bb6"&gt;How to: SonarQube Code Coverage in Jenkins with Maven Project&lt;/a&gt;, you can leave the steps related to the SonarQube.&lt;/li&gt;
&lt;li&gt;Empty &lt;code&gt;Jenkinsfile&lt;/code&gt; (assuming you make the project skeleton as I did).&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Before doing this tutorial
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Make sure you don't run any services (ExpressJS service, NestJS service, and Front-end service) on your local.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Configuring Jenkins for Node project
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;To configure Jenkins for Node, go to your Jenkins UI. Mine is at &lt;code&gt;http://localhost:8085&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;From &lt;code&gt;Dashboard&lt;/code&gt;, go to &lt;code&gt;Manage Jenkins&lt;/code&gt; which you can find on left navigation bar.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1sea27bfurinx8pj6kmo.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Choose &lt;code&gt;Plugins&lt;/code&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyh55pgbcior14kpqiimj.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Go to &lt;code&gt;Available plugins&lt;/code&gt; section and type &lt;code&gt;NodeJS Plugin&lt;/code&gt;. I've already installed the plugin, so I cannot show you the step, but this is the plugin:
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftruyv6l893f46fn7kepd.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Back to &lt;code&gt;Manage Jenkins&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Choose &lt;code&gt;Tools&lt;/code&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffz6gvsj8g0uzm9gudszv.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Scroll until you see &lt;code&gt;NodeJS installations&lt;/code&gt;. Don't mind the &lt;code&gt;edited&lt;/code&gt;, because I already have configured mine.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcrgof5bfkcmot8i72awx.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Choose &lt;code&gt;Add NojeJS&lt;/code&gt;. You can fill the field like mine. I preferred to use my locally installed Node instead of download it from Jenkins environment:
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh03jswakgaozjeifu38l.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;code&gt;Save&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;(OPTOPNAL) Check if your NodeJS is correctly configured at Jenkins. Choose &lt;code&gt;New Item&lt;/code&gt; located above &lt;code&gt;Manage Jenkins&lt;/code&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fabemzmiwul0s1tvm5lhn.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Enter a name something like &lt;code&gt;test-node-configuration&lt;/code&gt;. Choose &lt;code&gt;Pipeline project&lt;/code&gt;. Then click &lt;code&gt;OK&lt;/code&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3hskydemkn2n646bsfiz.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In &lt;code&gt;Pipeline&lt;/code&gt; section, add below script and click &lt;code&gt;Save&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pipeline {
    agent any
    tools {
        nodejs 'NodeJS'
    }
    stages {
        stage('Check NPM Version') {
            steps {
                bat 'npm version'
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1tuc0pbtfjojpvuypfyt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1tuc0pbtfjojpvuypfyt.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You will get redirected into you newly created project. Click &lt;code&gt;Build Now&lt;/code&gt;. Then you will see you project being built.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjim8nqhyqf29e1q4pbiv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjim8nqhyqf29e1q4pbiv.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When it's done, click on &lt;code&gt;#1&lt;/code&gt;.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp9gxy9zs55kzrh787v1a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp9gxy9zs55kzrh787v1a.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Choose &lt;code&gt;Console Output&lt;/code&gt;. And you will see some logs. If your Node configured correctly, you will see this on your console output:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwf71qgeekjl9x2pvo0ti.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwf71qgeekjl9x2pvo0ti.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Upload .env files from every service to Jenkins
&lt;/h2&gt;

&lt;p&gt;Since we are depending on &lt;code&gt;.env&lt;/code&gt; file for every service, we need to upload it into Jenkins server.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;code&gt;Manage Jenkins&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;In &lt;code&gt;Security&lt;/code&gt; section, choose &lt;code&gt;Credentials&lt;/code&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhkq3ibh5va4uqa4mr5cp.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;code&gt;System&lt;/code&gt; on &lt;code&gt;Stores scoped to Jenkins&lt;/code&gt; section.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx0lzm3b1du0pwy8hlg5b.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Choose &lt;code&gt;Global credentials (unrestricted)&lt;/code&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fixebrk8n2qouqwjzmiyv.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click on &lt;code&gt;Add Credentials&lt;/code&gt;.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcu8mgtqzey4zv8i75hjw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcu8mgtqzey4zv8i75hjw.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click dropdown in &lt;code&gt;Kind&lt;/code&gt; section. Choose &lt;code&gt;Secret File&lt;/code&gt;.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft6f57mqtnmq7oyc9zkma.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft6f57mqtnmq7oyc9zkma.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click at &lt;code&gt;Choose File&lt;/code&gt; (mine is Pilih File).&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbe52c42vtd8ichuwywp2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbe52c42vtd8ichuwywp2.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For first &lt;code&gt;.env&lt;/code&gt;, choose from &lt;code&gt;express-service&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Give it some ID so you won't forget what this is &lt;code&gt;.env&lt;/code&gt; for. Mine is: &lt;code&gt;customer-express-env&lt;/code&gt;. Don't mind the warning, because I already have it configured.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frz74j3kb5tdbt3iv4g94.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frz74j3kb5tdbt3iv4g94.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click &lt;code&gt;Create&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Do the same for &lt;code&gt;.env&lt;/code&gt; in &lt;code&gt;nest-service&lt;/code&gt; service and &lt;code&gt;front-end-service&lt;/code&gt;. I ID'd the &lt;code&gt;nest-service&lt;/code&gt; with &lt;code&gt;customer-nest-env&lt;/code&gt; and for &lt;code&gt;front-end-service&lt;/code&gt; I set the ID as &lt;code&gt;customer-react-env&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Writing Jenkinsfile
&lt;/h2&gt;

&lt;p&gt;This is actually very exciting for me because it is the first time I do CI with Jenkins by writing the Jenkinsfile first. I recommend you to write it in an IDE like Visual Studio Code, and if you use VS Code, I recommend you to install an extension called &lt;a href="https://marketplace.visualstudio.com/items?itemName=ivory-lab.jenkinsfile-support" rel="noopener noreferrer"&gt;JenkinsFile Support&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Defining the pipeline
&lt;/h3&gt;

&lt;p&gt;First we are going to create the Jenkinsfile pipeline.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pipeline{
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Defining agent
&lt;/h3&gt;

&lt;p&gt;Since I don't know much about Jenkins, I define the agent as &lt;code&gt;any&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;agent any
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Defining secret environment
&lt;/h3&gt;

&lt;p&gt;Since we need &lt;code&gt;.env&lt;/code&gt; to run all service, we have to provide the credential IDs for all &lt;code&gt;.env&lt;/code&gt; that we already upload on Jenkins Server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;environment {
    SECRET_FILE_EXPRESS = credentials("customer-express-env")
    SECRET_FILE_NEST = credentials("customer-nest-env")
    SECRET_FILE_REACT = credentials("customer-react-env")
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Defining tool
&lt;/h3&gt;

&lt;p&gt;We have to define the tools to build the project. Which is NodeJS.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tools {
    nodejs "NodeJS" // change the NodeJS with the name you give when you configuring tools of NodeJS in Jenkins Server
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Defining stages
&lt;/h3&gt;

&lt;p&gt;We are going to have few stages.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Creating &lt;code&gt;.env&lt;/code&gt; file for every services.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    stage("Create ExpressJs Service ENV"){
        steps{
          dir("express-service") {
            script {
              withCredentials([file(credentialsId: "customer-express-env", variable: "SECRET_FILE_EXPRESS")]) {
                writeFile file: '.env', text: readFile(file: "${SECRET_FILE_EXPRESS}")
              }
            }
          }
        }
    }
    stage("Create NestJs Service ENV"){
      steps{
        dir("nest-service") {
          script {
            withCredentials([file(credentialsId: "customer-nest-env", variable: "SECRET_FILE_NEST")]) {
              writeFile file: '.env', text: readFile(file: "${SECRET_FILE_NEST}")
            }
          }
        }
      }
    }
    stage("Create React Service ENV"){
      steps{
        dir("front-end-service") {
          script {
            withCredentials([file(credentialsId: "customer-react-env", variable: "SECRET_FILE_REACT")]) {
              writeFile file: '.env', text: readFile(file: "${SECRET_FILE_REACT}")
            }
          }
        }
      }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Running all service in parallel because if we defined it in sequence, Jenkins build will stuck on what ever run first, so this is how:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    stage("Build ExpressJs and NestJs Service") {
      steps {
        parallel (
          "run express" : {
            dir("express-service") {
              bat "npm install"
              bat "node -r dotenv/config index.js"
              bat "node -r dotenv/config src/configs/db.config.js"
              bat "node index.js"
            }
          },
          "run nest": {
            dir("nest-service") {
              bat "npm install"
              bat "npm run start"
            }
          },
          "run react": {
            dir("front-end-service") {
              bat "npm install"
              bat "npm run dev"
            }
          }
        )
      }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Final look of Jenkinsfile
&lt;/h2&gt;

&lt;p&gt;This is the final look of our &lt;code&gt;Jenkinsfile&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pipeline {
  agent any
  environment {
    SECRET_FILE_EXPRESS = credentials("customer-express-env")
    SECRET_FILE_NEST = credentials("customer-nest-env")
    SECRET_FILE_REACT = credentials("customer-react-env")
  }
  tools {
    nodejs "NodeJS"
  }
  stages {
    stage("Create ExpressJs Service ENV"){
        steps{
          dir("express-service") {
            script {
              withCredentials([file(credentialsId: "customer-express-env", variable: "SECRET_FILE_EXPRESS")]) {
                writeFile file: '.env', text: readFile(file: "${SECRET_FILE_EXPRESS}")
              }
            }
          }
        }
    }
    stage("Create NestJs Service ENV"){
      steps{
        dir("nest-service") {
          script {
            withCredentials([file(credentialsId: "customer-nest-env", variable: "SECRET_FILE_NEST")]) {
              writeFile file: '.env', text: readFile(file: "${SECRET_FILE_NEST}")
            }
          }
        }
      }
    }
    stage("Create React Service ENV"){
      steps{
        dir("front-end-service") {
          script {
            withCredentials([file(credentialsId: "customer-react-env", variable: "SECRET_FILE_REACT")]) {
              writeFile file: '.env', text: readFile(file: "${SECRET_FILE_REACT}")
            }
          }
        }
      }
    }
    stage("Build ExpressJs and NestJs Service") {
      steps {
        parallel (
          "run express" : {
            dir("express-service") {
              bat "npm install"
              bat "node -r dotenv/config index.js"
              bat "node -r dotenv/config src/configs/db.config.js"
              bat "node index.js"
            }
          },
          "run nest": {
            dir("nest-service") {
              bat "npm install"
              bat "npm run start"
            }
          },
          "run react": {
            dir("front-end-service") {
              bat "npm install"
              bat "npm run dev"
            }
          }
        )
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Build Jenkins Project.
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Push the change you created in Jenkinsfile to your Git repository.&lt;/li&gt;
&lt;li&gt;Choose &lt;code&gt;New Item&lt;/code&gt; at Jenkins Dashboard.&lt;/li&gt;
&lt;li&gt;Enter the name for your Jenkins project. choose Pipeline. Click &lt;code&gt;OK&lt;/code&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fag4vnlds7loy970sqiql.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Once you got redirected to your project configuration, go to &lt;code&gt;Pipeline&lt;/code&gt; section.&lt;/li&gt;
&lt;li&gt;In Definition dropdown, choose &lt;code&gt;Pipeline script from SCM&lt;/code&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpldk9cjvf6civ5szac02.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;In SCM dropdown, choose &lt;code&gt;Git&lt;/code&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiqvhn6dj6ixmzs65pq7y.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Enter your repository url. The one that you use to clone your repository. I don't set any credential since my repository is public.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7nwgmurn9gye8nyuwvhu.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Choose the branch you want to build, mine is &lt;code&gt;main&lt;/code&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0zn7o7kpme5br6ldyf6h.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;I don't change anything at &lt;code&gt;Script Path&lt;/code&gt; section since my Jenkinsfile name is &lt;code&gt;Jenkinsfile&lt;/code&gt;. Please mind that it is case sensitive.&lt;/li&gt;
&lt;li&gt;Click &lt;code&gt;Save&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;You will be redirected to your project. Choose &lt;code&gt;Build Now&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If things are fine you will mostlikely see this:
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2xgunkmykhxqyu8aaj96.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Since we are running our project, Jenkins will keep on going on build stage. To see if we done it right, go to your build history. Click on the latest build, which is &lt;code&gt;#1&lt;/code&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foijbjdacecr24cbhmijl.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Choose &lt;code&gt;Console Output&lt;/code&gt;. Carefuly read the output to see if our services are running. If it does, try to go to front-end service address (still using the local address).&lt;/li&gt;
&lt;li&gt;You will see your own interface even when you're not running it in your local because it is being run by Jenkins Server.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr4qyktq0liyogv356ba3.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>jenkins</category>
      <category>ci</category>
      <category>node</category>
    </item>
    <item>
      <title>Customer: Use Case (4: Implementing Front-end Service)</title>
      <dc:creator>Amalia Hajarani</dc:creator>
      <pubDate>Thu, 30 Nov 2023 02:15:43 +0000</pubDate>
      <link>https://dev.to/amalja0/customer-use-case-4-implementing-front-end-service-5hha</link>
      <guid>https://dev.to/amalja0/customer-use-case-4-implementing-front-end-service-5hha</guid>
      <description>&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Installed Node version of &lt;code&gt;v20.10.0&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Brief Explanation
&lt;/h2&gt;

&lt;p&gt;In this user interface I will fetch data from two services that I've created before which are ExpressJS service and NestJs service. For forms validation needs, I use Formik and Yup to make it easier. Lastly, since I just create a single page application I thought I would be overkill to use Redux, hence I use &lt;code&gt;Lifting State Up&lt;/code&gt; approach from child to its parent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Initialize React-Vite application
&lt;/h2&gt;

&lt;p&gt;I'm assuming that you have already created project directories just like what I did in the first post.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open new &lt;code&gt;command prompt&lt;/code&gt; from the &lt;code&gt;front-end-service&lt;/code&gt; directory.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create new React using Vite by running command below. I choose &lt;code&gt;React&lt;/code&gt; as the framework and &lt;code&gt;JavaScript&lt;/code&gt; as the variant.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm create vite@latest .
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run command:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install these dependencies:&lt;/p&gt;
&lt;h4&gt;
  
  
  Material UI Dependencies
&lt;/h4&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm npm install @mui/material @emotion/react @emotion/styled
&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;
  
  
  Material UI Icon Dependencies
&lt;/h4&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install @mui/icons-material
&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;
  
  
  Axios dependency
&lt;/h4&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install axios
&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;
  
  
  Formik Yup dependency
&lt;/h4&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install formik yup
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;My &lt;code&gt;package.json&lt;/code&gt; dependencies looks like:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"dependencies": {
    "@emotion/react": "^11.11.1",
    "@emotion/styled": "^11.11.0",
    "@mui/icons-material": "^5.14.18",
    "@mui/material": "^5.14.18",
    "axios": "^1.6.2",
    "formik": "^2.4.5",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "yup": "^1.3.2"
},
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create project directory skeleton that looks like this:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;front-end-service/
├── src/
│   ├── assets/
│   ├── components/
│   ├── constants/
│   ├── services/
│   ├── utils/
│   ├── views/
│   ├── App.jsx
│   ├── index.css
│   ├── main.jsx
│   └── theme.js
├── .env
└── package.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;My &lt;code&gt;.env&lt;/code&gt; file looks like below. Makesure that you have correct endpoints and use &lt;code&gt;VITE&lt;/code&gt; as the prefix.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;VITE_BASE_URL_EXPRESS=http://localhost:3001
VITE_BASE_URL_NEST=http://localhost:3002
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Configuring global theme
&lt;/h2&gt;

&lt;p&gt;Since we are using Material UI, we have the independency to customize some components. I did few customization for typography, palette, and some components inside &lt;code&gt;theme.js&lt;/code&gt; as you can see below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { createTheme, responsiveFontSizes } from "@mui/material";

const firstLetterUppercase = {
  '::first-letter': { 
    textTransform: 'uppercase',
  }
}

let theme = createTheme({
  palette: {
    primary: {
      main:'#FF4669',
      contrastText: '#fff'
    }, 
    secondary: {
      main: '#F5F5F5',
      contrastText: '#BDBDBD'
    }
  },
  typography: {
    fontFamily: [
      'Poppins', 
      'sans-serif'
    ].join(',')
  },
  components: {
    MuiButton: {
      styleOverrides: {
        root: {
          textTransform: 'none',
          borderRadius: 20
        }
      }
    },
    MuiInputLabel: {
      styleOverrides: {
        root: firstLetterUppercase
      }
    },
    MuiFormHelperText: {
      styleOverrides: {
        root: firstLetterUppercase
      }
    }
  }
});

theme = responsiveFontSizes(theme);

export default theme;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since I use Google Font, I need to define it inside &lt;code&gt;index.css&lt;/code&gt;. My final &lt;code&gt;index.css&lt;/code&gt; is look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600&amp;amp;display=swap');

:root {
  font-family: 'Poppins', sans-serif;
}

body {
  background-color: #F5F5F5;
}

/* width */
::-webkit-scrollbar {
  width: 10px;
}

/* Handle */
::-webkit-scrollbar-thumb {
  background: #FF4669;
  border-radius: 10px;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, to use our costumized theme, we have to define &lt;code&gt;ThemeProvider&lt;/code&gt; in &lt;code&gt;App.jsx&lt;/code&gt;. My final &lt;code&gt;App.jsx&lt;/code&gt; looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { ThemeProvider } from '@emotion/react'
import theme from './theme'
import Dashboard from './views/Dashboard'

function App() {

  return (
    &amp;lt;ThemeProvider theme={theme}&amp;gt;
      &amp;lt;Dashboard /&amp;gt;
    &amp;lt;/ThemeProvider&amp;gt;
  )
}

export default App
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating constants
&lt;/h2&gt;

&lt;p&gt;As I've said, I'll be fetching data from two services, to make it more consistent, I will create a file called &lt;code&gt;constants.js&lt;/code&gt; inside &lt;code&gt;constants&lt;/code&gt; directory with content like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const Constant = {
  EXPRESS_ID: 0,
  NEST_ID: 1
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating services and its helper function
&lt;/h2&gt;

&lt;p&gt;Before we are creating some services to fetch data from APIs, I'll create a helper function to define the base url based on which service the data will be fetched. On &lt;code&gt;utils&lt;/code&gt; directory, create a file called &lt;code&gt;helperFunction.js&lt;/code&gt; and this is how it looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Constant } from "../constants/constants"

export const defineBaseUrl = (serviceId) =&amp;gt; {
  const BASE_URL = serviceId === Constant.EXPRESS_ID ? import.meta.env.VITE_BASE_URL_EXPRESS : import.meta.env.VITE_BASE_URL_NEST;

  return BASE_URL;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we are going to create a file called &lt;code&gt;index.js&lt;/code&gt; inside &lt;code&gt;services&lt;/code&gt; directory. As you might notice, we have same endpoints for both ExpressJS and NestJS service just different host. So here's how I creating my service so I could fetch the data from the wanted service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import axios from "axios";
import { defineBaseUrl } from "../utils/helperFunction";

let BASE_URL = "";
let BASE_PREFIX = "/api/customers"

export const createCustomer = async (serviceId, requestBody) =&amp;gt; {
  BASE_URL = defineBaseUrl(serviceId);

  try {
    const res = await axios.post(BASE_URL + BASE_PREFIX, requestBody);
    return res;
  } catch (error) {
    console.log(error);
  }
};

export const getCustomers = async (serviceId) =&amp;gt; {
  BASE_URL = defineBaseUrl(serviceId);

  try {
    const res = await axios.get(BASE_URL + BASE_PREFIX);
    return res;
  } catch (error) {
    console.log(error);
  }
};

export const getCustomerById = async (serviceId, customerId) =&amp;gt; {
  BASE_URL = defineBaseUrl(serviceId);

  try {
    const res = await axios.get(BASE_URL + BASE_PREFIX + `/${customerId}`);
    return res;
  } catch (error) {
    console.log(error);
  }
}

export const updateCustomer = async (serviceId, customerId, bodyRequest) =&amp;gt; {
  BASE_URL = defineBaseUrl(serviceId);

  try {
    const res = await axios.put(BASE_URL + BASE_PREFIX + `/${customerId}`, bodyRequest);
    return res;
  } catch (error) {
    console.log(error);
  }
};

export const deleteCustomer = async (serviceId, customerId) =&amp;gt; {
  BASE_URL = defineBaseUrl(serviceId);

  try {
    const res = await axios.delete(BASE_URL + BASE_PREFIX + `/${customerId}`);
    return res;
  } catch (error) {
    console.log(error);
  }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating components
&lt;/h2&gt;

&lt;p&gt;To make it more structured, I'm going to create some components first.&lt;/p&gt;

&lt;h3&gt;
  
  
  Table of costumers component
&lt;/h3&gt;

&lt;p&gt;In this component, I'm using MUI's table components. In&lt;code&gt;components&lt;/code&gt; directory, create a new directory called &lt;code&gt;CustomerDataList&lt;/code&gt;, then create a new file called &lt;code&gt;index.jsx&lt;/code&gt;. It's a little tricky to do some loops because I want the user interface to use English as the language, but the response from backend is in Bahasa hence I do some tricky workaround. But the final look is like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { CloseOutlined, DeleteForever, Edit, SearchRounded } from '@mui/icons-material';
import { Grid, IconButton, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, TextField, Typography } from '@mui/material';
import { useFormik } from 'formik';
import React, { useEffect, useState } from 'react'
import * as Yup from 'yup';
import FormCustomerModal from '../FormCustomerModal';
import { deleteCustomer, getCustomerById, getCustomers, updateCustomer } from '../../services';

const header = [
  "ID",
  "Membership Number",
  "Name",
  "Address",
  "City",
  "Actions"
];

const structure = [
  "id",
  "no",
  "nama",
  "alamat",
  "kota",
  "aksi"
];

const initialValues = {
  id: '',
  no: '',
  nama: '',
  alamat: '',
  kota: ''
};

const validationSchema = Yup.object().shape({
  id: Yup.string(),
  no: Yup.string(),
  nama: Yup.string().required(),
  alamat: Yup.string().required(),
  kota: Yup.string().required(),
});

const CustomerDataList = ({ serviceID, onRefresh, onUpdate, onDelete }) =&amp;gt; {
  const [dataTable, setDataTable] = useState([]);
  const [isUpdating, setIsUpdating] = useState(false);
  const [searchedId, setSearchedId] = useState('');
  const [isSearching, setIsSearching] = useState(false);
  const [isDataNotFound, setIsDataNotFound] = useState(false);

  const updateCustomerFormik = useFormik({
    initialValues: initialValues,
    validationSchema: validationSchema,
    enableReinitialize: true,
    onSubmit: () =&amp;gt; {
      handleUpdateCustomer();
      setIsUpdating(false);
    }
  });

  const handleUpdateFormikValue = ( index ) =&amp;gt; {
    updateCustomerFormik.setValues(dataTable[index]);
    setIsUpdating(true)
  }

  const handleCloseUpdateModal = () =&amp;gt; {
    setIsUpdating(false);
  }

  const handleGetAllCustomers = () =&amp;gt; {
    getCustomers(serviceID)
      .then((res) =&amp;gt; {
        if (res.status === 200) {
          setDataTable([...res.data]);
        }
      }).catch((err) =&amp;gt; {
        console.log(err);
      });
  }

  const handleUpdateCustomer = () =&amp;gt; {
    const customerId = updateCustomerFormik.values.id;

    const requestBody = {
      nama: updateCustomerFormik.values.nama,
      alamat: updateCustomerFormik.values.alamat,
      kota: updateCustomerFormik.values.kota
    };

    updateCustomer(serviceID, customerId, requestBody)
      .then(() =&amp;gt; {
        onUpdate();
      }).catch((err) =&amp;gt; {
        console.log(err);
      });
  }

  const handleDeleteCustomer = ( index ) =&amp;gt; {
    const customerId = dataTable[index].id;

    deleteCustomer(serviceID, customerId)
      .then(() =&amp;gt; {
        onDelete()
      }).catch((err) =&amp;gt; {
        console.log(err);
      });
  }

  const handleSearchCustomer = () =&amp;gt; {
    if(searchedId !== '') {
      setIsSearching(true);
      getCustomerById(serviceID, searchedId)
        .then((res) =&amp;gt; {
          if (res.status === 200 &amp;amp;&amp;amp; Object.keys(res.data).length &amp;gt; 0) {
            setIsDataNotFound(false);
            setDataTable([res.data]);
          } else {
            setIsDataNotFound(true);
          }
        }).catch((err) =&amp;gt; {
          console.log(err);
        });
    }
  }

  const handleCloseSearching= () =&amp;gt; {
    setIsSearching(false);
    setSearchedId('');
    handleGetAllCustomers();
  }

  useEffect(() =&amp;gt; {
    handleGetAllCustomers();
  }, [serviceID, onRefresh, isUpdating, onDelete]);

  useEffect(() =&amp;gt; {
    setSearchedId('');
  }, [serviceID]);

  const ActionButtonGroups = ({ index }) =&amp;gt; {
    return (
      &amp;lt;Grid container&amp;gt;
        &amp;lt;Grid item&amp;gt;
          &amp;lt;IconButton 
            color='warning'
            onClick={() =&amp;gt; handleUpdateFormikValue(index)}
          &amp;gt;
            &amp;lt;Edit /&amp;gt;
          &amp;lt;/IconButton&amp;gt;
        &amp;lt;/Grid&amp;gt;
        &amp;lt;Grid item&amp;gt;
          &amp;lt;IconButton 
            color='error'
            onClick={() =&amp;gt; handleDeleteCustomer(index)}
          &amp;gt;
            &amp;lt;DeleteForever /&amp;gt;
          &amp;lt;/IconButton&amp;gt;
        &amp;lt;/Grid&amp;gt;
      &amp;lt;/Grid&amp;gt;
    );
  }

  return (
    &amp;lt;&amp;gt;
      &amp;lt;TextField
        fullWidth
        variant='filled'
        placeholder='Search by id'
        value={searchedId}
        onChange={(e) =&amp;gt; setSearchedId(e.target.value)}
        sx={{
          backgroundColor: 'primary.main',
          borderTopLeftRadius: '8px',
          borderTopRightRadius: '8px',
        }}
        inputProps={{
          sx: {
            paddingY: '1rem',
            color: 'primary.contrastText'
          },
        }}
        InputProps={{
          endAdornment: (
            isSearching 
            ? (
              &amp;lt;IconButton
                sx={{ color: 'primary.contrastText' }}
                onClick={handleCloseSearching}
              &amp;gt;
                &amp;lt;CloseOutlined /&amp;gt;
              &amp;lt;/IconButton&amp;gt;
            ) : (
              &amp;lt;IconButton 
                sx={{ color: 'primary.contrastText' }}
                onClick={handleSearchCustomer}
              &amp;gt;
                &amp;lt;SearchRounded /&amp;gt;
              &amp;lt;/IconButton&amp;gt;
            )
          )
        }}
      /&amp;gt;
      &amp;lt;TableContainer component={Paper} sx={{ maxHeight: 375, boxShadow: 'none'}}&amp;gt;
        &amp;lt;Table sx={{ width: '100%' }} stickyHeader&amp;gt;
          &amp;lt;TableHead&amp;gt;
            &amp;lt;TableRow&amp;gt;
              {
                header.map((item, index) =&amp;gt; {
                  return (
                    &amp;lt;TableCell 
                      align="left" 
                      key={index} 
                      sx={{
                        color: '#6D6C6D',
                        borderBottom: '2px solid',
                        borderColor: 'primary.main'
                      }}
                    &amp;gt;
                      {item}
                    &amp;lt;/TableCell&amp;gt;
                  )
                })
              }
            &amp;lt;/TableRow&amp;gt;
          &amp;lt;/TableHead&amp;gt;
          &amp;lt;TableBody&amp;gt;
            {
              isSearching &amp;amp;&amp;amp; isDataNotFound 
              ? (
                &amp;lt;TableRow&amp;gt;
                  &amp;lt;TableCell colSpan={header.length} align={"center"}&amp;gt;
                    &amp;lt;Typography&amp;gt;No Customer Found&amp;lt;/Typography&amp;gt;
                  &amp;lt;/TableCell&amp;gt;
                &amp;lt;/TableRow&amp;gt;
              ) : (
                dataTable.map((row, index) =&amp;gt; {
                  return (
                    &amp;lt;TableRow key={index}&amp;gt;
                      {
                        structure.map((column) =&amp;gt; {
                          return (
                            &amp;lt;TableCell&amp;gt;
                              {
                                column === 'aksi' 
                                ? &amp;lt;ActionButtonGroups index={index} /&amp;gt;
                                : &amp;lt;Typography&amp;gt;{row[column.toLowerCase()]}&amp;lt;/Typography&amp;gt;
                              }
                            &amp;lt;/TableCell&amp;gt;
                          )
                        })
                      }
                    &amp;lt;/TableRow&amp;gt;
                  )
                })
              )
            }
          &amp;lt;/TableBody&amp;gt;
        &amp;lt;/Table&amp;gt;
      &amp;lt;/TableContainer&amp;gt;

      &amp;lt;FormCustomerModal 
        title={"Update Customer Data"}
        open={isUpdating}
        handleClose={handleCloseUpdateModal}
        formikProps={updateCustomerFormik}
        disabledField={[ "id", "no", "createdAt", "updatedAt" ]}
      /&amp;gt;
    &amp;lt;/&amp;gt;
  )
}

export default CustomerDataList;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Form dialogue component
&lt;/h3&gt;

&lt;p&gt;As you might notice in previous component, I have a component called FormCustomerModal. This component will be used as a dialogue containing form to add a customer and update an existing customer. First thing first, in &lt;code&gt;components&lt;/code&gt; directory, create a new folder called &lt;code&gt;FormCustomerModal&lt;/code&gt;, then inside that directory, create a new file called &lt;code&gt;index.jsx&lt;/code&gt;. The final look of this file is just like below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Button, Dialog, DialogContent, DialogTitle, Grid, TextField } from '@mui/material';
import React from 'react'

const FormCustomerModal = ({ title, open, handleClose, formikProps, disabledField=[] }) =&amp;gt; {
  return (
    &amp;lt;React.Fragment&amp;gt;
      &amp;lt;Dialog 
        open={open} 
        onClose={handleClose}
        fullWidth
      &amp;gt;
        &amp;lt;DialogTitle sx={{ textAlign: 'center' }}&amp;gt;{ title }&amp;lt;/DialogTitle&amp;gt;
        &amp;lt;DialogContent&amp;gt;
          &amp;lt;form onSubmit={formikProps.handleSubmit}&amp;gt;
            &amp;lt;Grid 
              container 
              spacing={2}
              flexDirection={'column'} 
              sx={{ padding: '0.5rem'}}
            &amp;gt;
              {
                Object.keys(formikProps.values).map((item, index) =&amp;gt; {
                  return (
                    &amp;lt;Grid item key={index}&amp;gt;
                      &amp;lt;TextField 
                        disabled={ disabledField.includes(item) ? true : false }
                        id="outlined-basic"
                        variant="outlined"
                        name={item}
                        label={item}
                        value={formikProps.values[item]}
                        onChange={formikProps.handleChange}
                        onBlur={formikProps.handleBlur}
                        error={formikProps.touched[item] &amp;amp;&amp;amp; formikProps.errors[item]}
                        helperText={formikProps.touched[item] &amp;amp;&amp;amp; formikProps.errors[item] &amp;amp;&amp;amp; `${item} cannot be empty`}
                        fullWidth
                      /&amp;gt;
                    &amp;lt;/Grid&amp;gt;
                  )
                })
              }
              &amp;lt;Grid item sx={{ alignSelf: 'center' }}&amp;gt;
                &amp;lt;Button variant='contained' type='submit'&amp;gt; Save Customer &amp;lt;/Button&amp;gt;
              &amp;lt;/Grid&amp;gt;
            &amp;lt;/Grid&amp;gt;
          &amp;lt;/form&amp;gt;
        &amp;lt;/DialogContent&amp;gt;
      &amp;lt;/Dialog&amp;gt;
    &amp;lt;/React.Fragment&amp;gt;
  )
}

export default FormCustomerModal;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating view of Dashboard
&lt;/h2&gt;

&lt;p&gt;Now we already have all of the components and service, we can safely create the view. In &lt;code&gt;views&lt;/code&gt; directory, create a folder called &lt;code&gt;Dashboard&lt;/code&gt; then create a new file called &lt;code&gt;indes.jsx&lt;/code&gt;. This is the content of our dashboard view:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Alert, Button, Container, Grid, Tab, Tabs, Typography } from '@mui/material'
import React, { useEffect, useState } from 'react'
import CustomerDataList from '../../components/CustomerDataList'
import { AddCircleOutlineRounded } from '@mui/icons-material'
import * as Yup from 'yup';
import { useFormik } from 'formik';
import FormCustomerModal from '../../components/FormCustomerModal';
import { createCustomer } from '../../services';
import { Constant } from '../../constants/constants';

const initialValues = {
  nama: '',
  alamat: '',
  kota: ''
};

const validationSchema = Yup.object().shape({
  nama: Yup.string().required(),
  alamat: Yup.string().required(),
  kota: Yup.string().required(),
})

const Dashboard = () =&amp;gt; {
  const [tabValue, setTabValue] = useState(0);
  const [isAddingNewCustomer, setIsAddingNewCustomer] = useState(false);
  const [refreshTable, setRefreshTable] = useState(true);
  const [alert, setAlert] = useState({
    open: false,
    severity: '',
    message: ''
  });

  const handleTabChange = (e, newValue) =&amp;gt; {
    setTabValue(newValue);
  }

  const newCustomerFormik = useFormik({
    initialValues: initialValues,
    validationSchema: validationSchema,
    onSubmit: (values) =&amp;gt; {
      handleAddNewCustomer(values);
    }
  });

  const handleCloseAddingNewCustomer = () =&amp;gt; {
    setIsAddingNewCustomer(false);
  }

  const handleAddNewCustomer = async (values) =&amp;gt; {
    createCustomer(tabValue, values)
      .then(() =&amp;gt; {
        setRefreshTable(true);
        setAlert({
          open: true,
          severity: 'success',
          message: `Successfully creating customer using Service ${tabValue === Constant.EXPRESS_ID ? 'ExpressJs': 'NestJs'}`
        })
      }).catch((err) =&amp;gt; {
        setRefreshTable(false);
      }).finally(() =&amp;gt; {
        setIsAddingNewCustomer(false);
      });
  }

  const handleUpdateCustomer = () =&amp;gt; {
    setAlert({
      open: true,
      severity: 'success',
      message: `Successfully updating customer using Service ${tabValue === Constant.EXPRESS_ID ? 'ExpressJs': 'NestJs'}`
    })
  }

  const handleDeleteCustomer = () =&amp;gt; {
    setAlert({
      open: true,
      severity: 'success',
      message: `Successfully deleting customer using Service ${tabValue === Constant.EXPRESS_ID ? 'ExpressJs': 'NestJs'}`
    })
  }

  useEffect(() =&amp;gt; {
    if (alert.open) {
      const timeOut = setTimeout(() =&amp;gt; {
        setAlert({
          open: false,
          severity: '',
          message: ''
        });
      }, 3000)

      return () =&amp;gt; {
        clearTimeout(timeOut)
      }
    }
  }, [alert])

  return (
    &amp;lt;Container&amp;gt;
      &amp;lt;Alert 
        severity={alert.severity} 
        sx={{ visibility: alert.open ? 'visible' : 'hidden' }}
      &amp;gt; 
        {alert.message} 
      &amp;lt;/Alert&amp;gt;

      &amp;lt;Grid 
        container
        spacing={4}
        flexDirection={"column"}
        alignItems={"center"} 
        justifyContent={"center"} 
        height={'95vh'}
      &amp;gt;
        &amp;lt;Grid item width={"100%"}&amp;gt;
          &amp;lt;Typography variant={"h5"} textAlign={"center"}&amp;gt;Customer List&amp;lt;/Typography&amp;gt;
        &amp;lt;/Grid&amp;gt;
        &amp;lt;Grid item width={"100%"}&amp;gt;
          &amp;lt;Grid container spacing={4} flexDirection={"column"} paddingTop={0}&amp;gt;
            &amp;lt;Grid item width={"100%"}&amp;gt;
              &amp;lt;Tabs value={tabValue} onChange={handleTabChange} centered&amp;gt;
                &amp;lt;Tab label="ExpressJS" sx={{ flexGrow: 1}} /&amp;gt;
                &amp;lt;Tab label="NestJS" sx={{ flexGrow: 1}} /&amp;gt;
              &amp;lt;/Tabs&amp;gt;
            &amp;lt;/Grid&amp;gt;
            &amp;lt;Grid item&amp;gt;
              &amp;lt;CustomerDataList 
                serviceID={tabValue} 
                onRefresh={() =&amp;gt; setRefreshTable(refreshTable)}
                onUpdate={() =&amp;gt; handleUpdateCustomer()}
                onDelete={() =&amp;gt; handleDeleteCustomer()}
              /&amp;gt;
            &amp;lt;/Grid&amp;gt;
            &amp;lt;Grid item alignSelf={"flex-end"}&amp;gt;
              &amp;lt;Button 
                variant='contained' 
                startIcon={&amp;lt;AddCircleOutlineRounded /&amp;gt;}
                onClick={() =&amp;gt; setIsAddingNewCustomer(true)}
              &amp;gt;
                Add Customer
              &amp;lt;/Button&amp;gt;
            &amp;lt;/Grid&amp;gt;
          &amp;lt;/Grid&amp;gt;
        &amp;lt;/Grid&amp;gt;
      &amp;lt;/Grid&amp;gt;

      &amp;lt;FormCustomerModal 
        title={'Add New Customer'}
        open={isAddingNewCustomer}
        handleClose={handleCloseAddingNewCustomer}
        formikProps={newCustomerFormik}
      /&amp;gt;
    &amp;lt;/Container&amp;gt;
  )
}

export default Dashboard;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Running application
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Makesure you run the ExpressJS and NestJS service.&lt;/li&gt;
&lt;li&gt;Back to &lt;code&gt;front-end-service&lt;/code&gt; root directory, open command prompt directed to the directory.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;npm run dev&lt;/code&gt;. You will see a localhost or similar address where you can see the interface.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If it is running correctly you will see this kind of user interface along with the functionality that we already build! &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3ynqfknz826fqkkemz2w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3ynqfknz826fqkkemz2w.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;To makesure that we did hit both services, we can see it at &lt;code&gt;Network&lt;/code&gt; section while inspecting element like when I'm at ExpressJS tab, I have this:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F131cqgn3wcpwm78azsmo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F131cqgn3wcpwm78azsmo.png" alt="Image description"&gt;&lt;/a&gt;&lt;br&gt;
When I move to the NestJS tab I have this:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffza1xfkm9fbanv5bs8ue.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffza1xfkm9fbanv5bs8ue.png" alt="Image description"&gt;&lt;/a&gt;&lt;br&gt;
If you notice, the first one is hitting &lt;code&gt;http://localhost:3001&lt;/code&gt; which is the ExpressJS service and the second one is hitting &lt;code&gt;http://localhost:3002&lt;/code&gt; which is the NestJS service.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>vite</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Customer: Use Case (3: Implementing NestJS Service)</title>
      <dc:creator>Amalia Hajarani</dc:creator>
      <pubDate>Thu, 30 Nov 2023 01:03:45 +0000</pubDate>
      <link>https://dev.to/amalja0/customer-use-case-3-implementing-nestjs-service-52ip</link>
      <guid>https://dev.to/amalja0/customer-use-case-3-implementing-nestjs-service-52ip</guid>
      <description>&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Installed Node version of &lt;code&gt;v20.10.0&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Installed PostgreSQL version of &lt;code&gt;16&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Initialize NestJS application
&lt;/h2&gt;

&lt;p&gt;I'm assuming that you have already created project directories just like what I did in the first post.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Install nest cli globally using &lt;code&gt;command prompt&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; npm i -g @nestjs/cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Open new &lt;code&gt;command prompt&lt;/code&gt; from the &lt;code&gt;nest-service&lt;/code&gt; directory.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create new NestJS project by running command below. I choose &lt;code&gt;npm&lt;/code&gt; as the package manager.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nest new .
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install these dependencies:&lt;/p&gt;
&lt;h4&gt;
  
  
  Dependency to work with .env file
&lt;/h4&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm i --save @nestjs/config
&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;
  
  
  TypeOrm dependencies
&lt;/h4&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install @nestjs/typeorm typeorm
&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;
  
  
  PostgreSQL dependency
&lt;/h4&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install pg
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;My &lt;code&gt;package.json&lt;/code&gt; dependencies looks like:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; "dependencies": {
    "@nestjs/common": "^10.0.0",
    "@nestjs/config": "^3.1.1",
    "@nestjs/core": "^10.0.0",
    "@nestjs/platform-express": "^10.0.0",
    "@nestjs/typeorm": "^10.0.1",
    "pg": "^8.11.3",
    "reflect-metadata": "^0.1.13",
    "rxjs": "^7.8.1",
    "typeorm": "^0.3.17"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create project directory skeleton that looks like this:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nest-service/
├── src/
│   ├── config/
│   ├── controllers/
│   ├── entities/
│   ├── modules/
│   ├── services/
│   ├── app.controller.ts
│   ├── app.module.ts
│   ├── app.service.ts
│   └── main.ts
├── .env
└── package.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;My &lt;code&gt;.env&lt;/code&gt; file looks like below. Makesure that you have correct and working port and credentials.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PORT=3002
DB_HOST=localhost
DB_PORT=5432
DB_USERNAME=postgres
DB_PASSWORD=password
DB_NAME=customer
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Creating configuration
&lt;/h2&gt;

&lt;p&gt;Inside &lt;code&gt;config&lt;/code&gt; directory, create a file named &lt;code&gt;database.config.ts&lt;/code&gt; the content will look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { PostgresConnectionOptions } from "typeorm/driver/postgres/PostgresConnectionOptions";
import { ConfigService } from '@nestjs/config';

export const databaseConfig = async (configService: ConfigService): Promise&amp;lt;PostgresConnectionOptions&amp;gt; =&amp;gt; ({
  type: 'postgres',
  host: configService.get&amp;lt;string&amp;gt;('DB_HOST'),
  port: parseInt(configService.get&amp;lt;string&amp;gt;('DB_PORT')),
  username: configService.get&amp;lt;string&amp;gt;('DB_USERNAME'),
  password: configService.get&amp;lt;string&amp;gt;('DB_PASSWORD'),
  database: configService.get&amp;lt;string&amp;gt;('DB_NAME'),
  entities: ["dist/**/*.entity{.ts,.js}"],
  synchronize: false,
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  creating entities
&lt;/h2&gt;

&lt;p&gt;Now, let's move on to &lt;code&gt;entities&lt;/code&gt; directory. Create a file called &lt;code&gt;Customer.enitity.ts&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn } from "typeorm";

@Entity('customers')
export class Customer {

  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  no: string;

  @Column()
  nama: string;

  @Column()
  alamat: string

  @Column()
  kota: string

  @CreateDateColumn()
  createdAt: Date

  @UpdateDateColumn()
  updatedAt: Date;

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating services
&lt;/h2&gt;

&lt;p&gt;In services we are going to create a file named &lt;code&gt;customer.service.ts&lt;/code&gt; and the content is looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { randomUUID } from "crypto";
import { Customer } from "src/entities/Customer.entity";
import { Repository } from "typeorm";

export interface CustomerInterface {
  id?: string,
  no?: string, 
  nama: string,
  alamat: string,
  kota: string
  createdAt?: Date;
  updatedAt?: Date;
};

@Injectable()
export class CustomersService {

  constructor(
    @InjectRepository(Customer)
    private customerRepository: Repository&amp;lt;CustomerInterface&amp;gt;
  ) {}

  create(customer: CustomerInterface): Promise&amp;lt;CustomerInterface&amp;gt; {
    const customerBody = this.customerRepository.create(customer);
    customerBody.no = randomUUID();
    customerBody.createdAt = new Date();
    customerBody.updatedAt = new Date();
    return this.customerRepository.save(customerBody);
  }

  findAll(): Promise&amp;lt;CustomerInterface[]&amp;gt; {
    return this.customerRepository.find();
  }

  findById(id: string): Promise&amp;lt;CustomerInterface&amp;gt; {
    return this.customerRepository
      .findOne({
        where: {
          id: id
        }
      });
  }

  update(id: string, data: CustomerInterface): Promise&amp;lt;any&amp;gt; {
    return this.customerRepository
      .createQueryBuilder()
      .update()
      .set({
        nama: data.nama,
        alamat: data.alamat,
        kota: data.kota
      })
      .where('id = :id', { id })
      .execute();
  }

  delete(id: string): Promise&amp;lt;any&amp;gt; {
    return this.customerRepository
      .createQueryBuilder()
      .delete()
      .from(Customer)
      .where('id = :id', { id })
      .execute();
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating controllers
&lt;/h2&gt;

&lt;p&gt;Let's create the controller for customer by making a file called &lt;code&gt;customer.controller.ts&lt;/code&gt; inside &lt;code&gt;controllers&lt;/code&gt; directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Body, Controller, Delete, Get, Param, Post, Put } from "@nestjs/common";
import { CustomerInterface, CustomersService } from "src/services/customer.service";

interface CreateCustomerDto {
  nama: string,
  alamat: string,
  kota: string
};

@Controller('api/customers')
export class CustomersController {

  constructor(private customerService: CustomersService) {}

  @Post()
  async create(@Body() createCustomerDto: CreateCustomerDto) {
    const customer = await this.customerService.create(createCustomerDto);

    if(!customer) {
      return 'Error creating customer';
    }

    return 'Customer created successfully';
  }

  @Get()
  async findAll() {
    const customers: Array&amp;lt;CustomerInterface&amp;gt; = await this.customerService.findAll();

    return customers;
  }

  @Get(':id')
  async findByNama(@Param('id') id: string ) {
    const customer: CustomerInterface = await this.customerService.findById(id);

    return customer;
  }

  @Put(':id')
  async update(@Param('id') id: string, @Body() body: CustomerInterface) {
    const newCustomer: CustomerInterface = await this.customerService.update(id, body);

    if(!newCustomer) {
      return 'Error updating customer';
    }

    return 'Customer updated successfully';
  }

  @Delete(':id')
  async remove(@Param('id') id: string) {
    await this.customerService.delete(id);

    return 'Successfully deleted customer with id' + id;
  }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating module
&lt;/h2&gt;

&lt;p&gt;Last, we need to pool our &lt;code&gt;entity&lt;/code&gt;, &lt;code&gt;controller&lt;/code&gt;, and &lt;code&gt;service&lt;/code&gt; at the &lt;code&gt;module&lt;/code&gt;. Create a file called &lt;code&gt;customer.module.ts&lt;/code&gt; inside &lt;code&gt;modules&lt;/code&gt; directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { CustomersController } from 'src/controllers/customer.controller';
import { Customer } from 'src/entities/Customer.entity';
import { CustomersService } from 'src/services/customer.service';

@Module({
  imports: [TypeOrmModule.forFeature([Customer])],
  controllers: [CustomersController],
  providers: [CustomersService]
})

export class CustomersModule {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configuring customer module in main module
&lt;/h2&gt;

&lt;p&gt;In &lt;code&gt;app.module.ts&lt;/code&gt; file which located in root directory, we will configuring the things we've created like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
import { CustomersModule } from './modules/customer.module';
import { databaseConfig } from './config/database.config';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true
    }),
    TypeOrmModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: async (configService: ConfigService) =&amp;gt; await databaseConfig(configService),
    }),
    CustomersModule
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Running application
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Back to &lt;code&gt;nest-service&lt;/code&gt; root directory, open command prompt directed to the directory.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;npm run start&lt;/code&gt;. It will take a little longer.&lt;/li&gt;
&lt;li&gt;If it is running correctly you will see this log:
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pxA6pT-a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j8bku5x0oe5nvwki2zpu.png" alt="Image description" width="800" height="348"&gt;
&lt;/li&gt;
&lt;li&gt;Test your API endpoints using Postman or Thunder Client.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>beginners</category>
      <category>tutorial</category>
      <category>typescript</category>
      <category>nestjs</category>
    </item>
    <item>
      <title>Customer: Use Case (2: Implementing ExpressJS Service)</title>
      <dc:creator>Amalia Hajarani</dc:creator>
      <pubDate>Wed, 29 Nov 2023 05:39:47 +0000</pubDate>
      <link>https://dev.to/amalja0/customer-use-case-2-implementing-expressjs-service-1735</link>
      <guid>https://dev.to/amalja0/customer-use-case-2-implementing-expressjs-service-1735</guid>
      <description>&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Installed Node version of &lt;code&gt;v20.10.0&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Installed PostgreSQL version of &lt;code&gt;16&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Database viewer, I use DBeaver&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Creating customer table
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Open DBeaver application&lt;/li&gt;
&lt;li&gt;Click &lt;code&gt;Database&lt;/code&gt; then choose &lt;code&gt;New Database Connection&lt;/code&gt; assuming you haven't connect to PostgreSQL database.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhcuu0fe2c9g0omyvynym.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Choose &lt;code&gt;PostgreSQL&lt;/code&gt;. Then click &lt;code&gt;Next&lt;/code&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbgzdja4u3r9x3ts29og1.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;I'm using the default connection settings so just click &lt;code&gt;Finish&lt;/code&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi3opnxxqt46vjdwr5d3i.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;You will see the newly connected PostgreSQL on the left side navigator.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz2ywklz4oljzcd1f8388.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Click the dropdown arrow until you can see this:
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc5wf4rxtgukd22k9u7hc.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Click right at &lt;code&gt;Databases&lt;/code&gt;. Choose &lt;code&gt;Create New Database&lt;/code&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1xofzsdavlg7ncxysguj.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Fill the database name field with &lt;code&gt;customer&lt;/code&gt;. Then click OK.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2yftdaurzli3z5l5uzfp.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Click on database dropdown and now you will see your new database.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3nnyheob9fgfwvivabtg.png" alt="Image description"&gt;
If you did not see your new database, please follow &lt;a href="https://stackoverflow.com/a/60289190" rel="noopener noreferrer"&gt;this step&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Initialize express application
&lt;/h2&gt;

&lt;p&gt;I'm assuming that you have already created project directories just like what I did in the first post.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open a &lt;code&gt;command prompt&lt;/code&gt; from express-service root directory.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Init a npm project by using command of:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm init
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install these dependencies:&lt;/p&gt;
&lt;h4&gt;
  
  
  Basic dependencies
&lt;/h4&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```
npm install express dotenv cors body-parser
```
&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;
  
  
  Sequalize dependency
&lt;/h4&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```
npm install sequelize
```
&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;
  
  
  PostgreSQL dependency
&lt;/h4&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```
npm install pg pg-hstore
```
&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;
  
  
  UUID dependency
&lt;/h4&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```
npm install uuid
```
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;My package.json dependencies looks like:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"dependencies": {
    "body-parser": "^1.20.2",
    "cors": "^2.8.5",
    "dotenv": "^16.3.1",
    "express": "^4.18.2",
    "pg": "^8.11.3",
    "pg-hstore": "^2.3.4",
    "sequelize": "^6.35.1",
    "uuid": "^9.0.1"
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create project directory skeleton that looks like this:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;express-service/
    ├── src/
    │   ├── configs/
    │   ├── controllers/
    │   ├── models/
    │   └── routes/
    ├── .env
    ├── index.js
    ├── package.json
    └── server.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;My &lt;code&gt;.env&lt;/code&gt; file looks like below. Makesure that you have correct and working port and credentials.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PORT=3001
DB_HOST=localhost
DB_PORT=5432
DB_USERNAME=postgres
DB_PASSWORD=password
DB_NAME=customer
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Creating configs
&lt;/h2&gt;

&lt;p&gt;Inside &lt;code&gt;configs&lt;/code&gt; directory, create a file named &lt;code&gt;db.config.js&lt;/code&gt; the content will look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;require('dotenv').config();

module.exports = {
  HOST: process.env.DB_HOST,
  USER: process.env.DB_USERNAME,
  PASSWORD: process.env.DB_PASSWORD,
  DB: process.env.DB_NAME,
  dialect: "postgres",
  pool: {
    max: 5,
    min: 0,
    acquire: 30000,
    idle: 10000
  }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the configuration to connect our application to the database.&lt;/p&gt;

&lt;h2&gt;
  
  
  creating models
&lt;/h2&gt;

&lt;p&gt;Now, let's move on to out &lt;code&gt;models&lt;/code&gt; directory&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create a file called &lt;code&gt;customer.model.js&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module.exports = (sequalize, Sequalize) =&amp;gt; {
  const Customer = sequalize.define("customer", {
    no: {
      type: Sequalize.UUID
    }, 
    nama: {
      type: Sequalize.STRING
    },
    alamat: {
      type: Sequalize.STRING
    },
    kota: {
      type: Sequalize.STRING
    }
  });

  return Customer;
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create another file called &lt;code&gt;index.js&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const dbConfig = require("../configs/db.config");

const Sequalize = require("sequelize");
const sequalize = new Sequalize(dbConfig.DB, dbConfig.USER, dbConfig.PASSWORD, {
  host: dbConfig.HOST,
  dialect: 'postgres',
  operatorAliases: false,

  pool: {
    max: dbConfig.pool.max,
    min: dbConfig.pool.min,
    acquire: dbConfig.pool.acquire,
    idle: dbConfig.pool.idle
  }
});

const db = {};

db.Sequalize = Sequalize;
db.sequalize = sequalize;

db.customer = require("./customer.model")(sequalize, Sequalize);

module.exports = db;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Creating controllers
&lt;/h2&gt;

&lt;p&gt;After configuring our model let's create the controller for customer by making a file called &lt;code&gt;customer.controller.js&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { v4: uuidv4 } = require('uuid');
const db = require("../models");
const model = require("../models");
const Customer = model.customer;
const Op = db.Sequalize.Op;

exports.create = (req, res) =&amp;gt; {

  const customer = {
    no: uuidv4(),
    nama: req.body.nama,
    alamat: req.body.alamat,
    kota: req.body.kota
  };

  Customer.create(customer)
    .then((data) =&amp;gt; {
      res.send(data);
    }).catch((err) =&amp;gt; {
      res.status(500).send({
        message:
          err.message || "Some error occurred while creating the Customer."
      });
    });

};

exports.findAll = (req, res) =&amp;gt; {

  Customer.findAll()
    .then((data) =&amp;gt; {
      res.send(data);
    }).catch((err) =&amp;gt; {
      res.status(500).send({
        message:
          err.message || "Some error occurred while retrieving the Customer."
      });
    });

};

exports.findOne = (req, res) =&amp;gt; {

  const id = req.params.id;

  Customer.findByPk(id)
    .then((data) =&amp;gt; {
      res.send(data);
    }).catch((err) =&amp;gt; {
      res.status(400).send({
        message:
          "Error retrieving Customer with id=" + id
      });
    });

};

exports.update = (req, res) =&amp;gt; {

  const id = req.params.id;

  Customer.update(req.body, { where: { id: id } })
    .then((data) =&amp;gt; {
      if (data[0] === 1) {
        res.send("Successfully updated Customer with id " + id);
      } else {
        res.send("Cannot update Customer with id " + id);
      }
    }).catch((err) =&amp;gt; {
      res.status(500).send({
        message: "Error updating Customer with id=" + id
      });
    });

};

exports.delete = (req, res) =&amp;gt; {

  const id = req.params.id;

  Customer.destroy({ where: { id: id } })
    .then((data) =&amp;gt; {
      if (data === 1) {
        res.send("Successfully deleted Customer with id " + id);
      } else {
        res.send("Cannot delete Customer with id " + id);
      }
    }).catch((err) =&amp;gt; {
      res.status(500).send({
        message: "Error deleting Customer with id=" + id
      });
    });

};

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating routes
&lt;/h2&gt;

&lt;p&gt;Last, we need to create our route in &lt;code&gt;customer.route.js&lt;/code&gt; inside &lt;code&gt;route&lt;/code&gt; directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module.exports = app =&amp;gt; {
  const customers = require("../controllers/customer.controller");

  var router = require("express").Router();

  router.post("/", customers.create);

  router.get("/", customers.findAll);

  router.get("/:id", customers.findOne);

  router.put("/:id", customers.update);

  router.delete("/:id", customers.delete);

  app.use('/api/customers', router);
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configuring server
&lt;/h2&gt;

&lt;p&gt;In &lt;code&gt;server.js&lt;/code&gt; file which located in root directory, we will configuring all needed dependency and connect our application to database like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");

require('dotenv').config();

function startServer() {
  const app = express();

  app.use(cors());

  app.use(express.json());

  app.use(express.urlencoded({ extended: true }));

  const db = require("./src/models");

  db.sequalize.sync()
    .then(() =&amp;gt; {
      console.log("Synced db.");
    }).catch((err) =&amp;gt; {
      console.log("Failed to sync db: " + err.message);
    });

    require("./src/routes/customer.routes")(app);

  const PORT = process.env.PORT;
  app.listen(PORT, () =&amp;gt; {
    console.log(`Server running on port ${PORT}`);
  });
};

module.exports = { start: startServer };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configure index.js
&lt;/h2&gt;

&lt;p&gt;Since most functionalities all already handled by &lt;code&gt;server.js&lt;/code&gt;, in &lt;code&gt;index.js&lt;/code&gt; we just have to call it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const server = require('./server');

server.start();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Running application
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Back to &lt;code&gt;express-service&lt;/code&gt; root directory, open command prompt directed to the directory.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;node index.js&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;If it running correctly you will see this log:
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbcktsoiyf4modfc8uj6g.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Test your API endpoints using Postman or Thunder Client.&lt;/li&gt;
&lt;/ol&gt;

</description>
    </item>
    <item>
      <title>Customer: Use Case for Learning Simple CRUD on ExpressJS and NestJS and The CI Pipeline (Use Case Introduction)</title>
      <dc:creator>Amalia Hajarani</dc:creator>
      <pubDate>Wed, 29 Nov 2023 04:33:16 +0000</pubDate>
      <link>https://dev.to/amalja0/customer-use-case-for-learning-simple-crud-on-expressjs-and-nestjs-and-the-ci-pipeline-use-case-introduction-3mc8</link>
      <guid>https://dev.to/amalja0/customer-use-case-for-learning-simple-crud-on-expressjs-and-nestjs-and-the-ci-pipeline-use-case-introduction-3mc8</guid>
      <description>&lt;p&gt;Hello dear reader. Today I have new use case to learn ExpressJS and NestJS and I think it's quiet beginner friendly since I have no experience in NestJS. We are just going to create a simple CRUD application and save it into PostgreSQL database. You can find the working repository &lt;a href="https://github.com/amalja0/simple-mini-project"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;Before diving deeper, I want you to know that I will be using two different ORMs, Sequalize for ExpressJS and TypeOrm for NestJS. As I haven't work with node backend environment for so long, I have few resources that I read to build my application. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://www.bezkoder.com/node-express-sequelize-postgresql/"&gt;Node.js Express &amp;amp; PostgreSQL: CRUD Rest APIs example with Sequelize&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://betterprogramming.pub/clean-node-js-architecture-with-nestjs-and-typescript-34b9398d790f"&gt;Clean Node.js Architecture —With NestJs and TypeScript&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Please check them out since of course they have better explanation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Brief explanation
&lt;/h2&gt;

&lt;p&gt;This is a painless usecase once you know how each ORM works and since again, it just a simple CRUD application. Below is how I structured my &lt;code&gt;customer&lt;/code&gt;  data to be use on every services:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    id: number,
    no: string, //uuid
    nama: string, //nama means name
    alamat: string, //alamat means adddress
    kota: string //kota means city
    createdAt: Date,
    updatedAt: Date
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For summary, we are going to make same functionality for both service which are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create new customer&lt;/li&gt;
&lt;li&gt;Get all customers&lt;/li&gt;
&lt;li&gt;Get a customer by id&lt;/li&gt;
&lt;li&gt;Update a customer based on the id&lt;/li&gt;
&lt;li&gt;Delete a customer based on the id&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Project structure
&lt;/h2&gt;

&lt;p&gt;Since we are going to be running some CI (Continuous Integration) using Jenkins, this is my kind of project structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;simple-mini-project/
    ├── express-service/
    ├── nest-service/
    ├── front-end-service/
    └── Jenkinsfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Tips from a beginner:
&lt;/h2&gt;

&lt;p&gt;Since one of our goal is to have CI run, we will have to push our code to a Git repository. It's much better if you push your work everytime you create a new functionality so you don't have to worry about losing your job!&lt;/p&gt;

</description>
      <category>node</category>
      <category>beginners</category>
      <category>tutorial</category>
      <category>nestjs</category>
    </item>
    <item>
      <title>How to: Integrating Jenkins with Telegram Bot</title>
      <dc:creator>Amalia Hajarani</dc:creator>
      <pubDate>Fri, 24 Nov 2023 02:06:32 +0000</pubDate>
      <link>https://dev.to/amalja0/how-to-integrating-jenkins-with-telegram-bot-1hal</link>
      <guid>https://dev.to/amalja0/how-to-integrating-jenkins-with-telegram-bot-1hal</guid>
      <description>&lt;p&gt;Hallo dear everyone whoever read this post. Today I'd love to share my another learning journey with Jenkins. You can find the final repository &lt;a href="https://github.com/amalja0/jenkins-lokomotif-scheduler-report" rel="noopener noreferrer"&gt;in here&lt;/a&gt;. The use case itself actually from &lt;a href="https://dev.to/amalja0/how-to-sonarqube-code-coverage-in-jenkins-with-maven-project-3bb6"&gt;this post&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisite
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Installed Java version of 17 and have it configured in environment variable.&lt;/li&gt;
&lt;li&gt;Installed Maven version of 3.9.5 and have it configured in environment variable.&lt;/li&gt;
&lt;li&gt;Installed Jenkins (mine is Jenkins 2.426.1) along with the configuration. You may read the post I've attached above to know more on how to configure Jenkins environment.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Creating Telegram Bot
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Open Telegram application. I use Telegram Web.&lt;/li&gt;
&lt;li&gt;Search for &lt;code&gt;BotFather&lt;/code&gt;. 
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe3vc23b37mnuy6pow8kx.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Click on their profile. Click &lt;code&gt;Start&lt;/code&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3hxsba3av5rivtxa8idp.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Send them a chat of &lt;code&gt;/newbot.&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Give your bot a name.&lt;/li&gt;
&lt;li&gt;Create a username for your bot.&lt;/li&gt;
&lt;li&gt;They will give you an &lt;code&gt;access token&lt;/code&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7pnh6sfpwka1wgz8lsgc.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Getting Chat Id
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;As you can see from the image above, &lt;code&gt;BotFather&lt;/code&gt; give you a link to chat with your bot. Click that link. &lt;/li&gt;
&lt;li&gt;Click &lt;code&gt;Start&lt;/code&gt; button located at the right corner.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fawzhi8dpfd99wi3b12ou.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Send some chat like &lt;code&gt;hi&lt;/code&gt; to the bot
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6zg8o0dfhj0cco81ekf0.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Get the chat id by opening this link &lt;code&gt;https://api.telegram.org/bot&amp;lt;access token&amp;gt;/getUpdates&lt;/code&gt;. You will get a response like below. Copy the chat id to somewhere safe:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "ok": true,
  "result": [
    {
      "update_id": 948995789,
      "message": {
        "message_id": 3,
        "from": {
          "id": 5046210284,
          "is_bot": false,
          "first_name": "Lala",
          "username": "amaliahajarani",
          "language_code": "en"
        },
        "chat": {
          "id": 5046210284, // this is the chat id          "first_name": "Lala",
          "username": "amaliahajarani",
          "type": "private"
        },
        "date": 1700788242,
        "text": "hi"
      }
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Creating credentials for Telegram Bot access token and chat id.
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Open Jenkins UI, mine is at &lt;a href="http://localhost:8085/" rel="noopener noreferrer"&gt;http://localhost:8085/&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;In side bar, choose &lt;code&gt;Manage Jenkins&lt;/code&gt; section.&lt;/li&gt;
&lt;li&gt;Go to &lt;code&gt;Security&lt;/code&gt; section. Choose &lt;code&gt;Credentials&lt;/code&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqyowlw4qssly51zfsen7.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Scroll to the bottom. You will sew &lt;code&gt;Stores scoped to Jenkins&lt;/code&gt; section. Click &lt;code&gt;System&lt;/code&gt; on that table.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw816s54n3y6lpyyv5ehv.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;You will be redirected into another page. Choose &lt;code&gt;Global credentials (unrestricted)&lt;/code&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Futa5gl7exzeokl01jy3d.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;code&gt;Add Credentials&lt;/code&gt; button.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ad6dn61zgbw2mrhqsxn.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;First, we are going to create credentials for access token. For &lt;code&gt;Kind&lt;/code&gt; dropdown, choose &lt;code&gt;Secret text&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Enter your access token into &lt;code&gt;Secret&lt;/code&gt; section. Then, enter some identifier at &lt;code&gt;ID&lt;/code&gt; field so you know what kind of credential is that. Then click &lt;code&gt;Create&lt;/code&gt; button.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffy3if3bndfl6pm3jj3i6.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Now, with the same step from poin &lt;strong&gt;6&lt;/strong&gt;, create new credential for chat id.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Creating JenkinsFile
&lt;/h2&gt;

&lt;p&gt;If you don't have JenkinsFile in your project yet, in the root of the project, create a new file without extensions called &lt;code&gt;JenkinsFile&lt;/code&gt; (better use code editor like Visual Studio Code). Mine lookslike this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pipeline{
    agent any
    environment {
        TELEGRAM_TOKEN = credentials('telegram-token') // change this line with your credential id for Telegram bot access token
        TELEGRAM_CHAT_ID = credentials('telegram-chat-id') // change this line with your credential id for Telegram bot chat id

        TEXT_PRE_BUILD = "Jenkins is building ${JOB_NAME}"
        TEXT_SUCCESS_BUILD = "${JOB_NAME} is Success"
        TEXT_FAILURE_BUILD = "${JOB_NAME} is Failure"
        TEXT_ABORTED_BUILD = "${JOB_NAME} is Aborted"
    }
    tools {
        maven 'MAVEN_HOME' 
    }
    stages{
        stage("Pre-Build"){
            steps{
                bat ''' curl -s -X POST https://api.telegram.org/bot"%TELEGRAM_TOKEN%"/sendMessage -d chat_id="%TELEGRAM_CHAT_ID%" -d text="%TEXT_PRE_BUILD%" '''
            }
        }
        stage("build"){
            steps{
                bat 'mvn clean install'
            }
        }
        stage('SonarQube analysis'){
            steps{
                withSonarQubeEnv(credentialsId:'jenkins-sonarqube', installationName: 'SonarQube') {
                    bat 'mvn sonar:sonar'
                }
            }
        }
        stage('SQuality Gate') {
            steps {
                timeout(time: 1, unit: 'MINUTES') {
                    waitForQualityGate abortPipeline: true
                }
            }
        }
    }
    post{
        success{
            script {
                bat ''' curl -s -X POST https://api.telegram.org/bot"%TELEGRAM_TOKEN%"/sendMessage -d chat_id="%TELEGRAM_CHAT_ID%" -d text="%TEXT_SUCCESS_BUILD%" '''
            }
        }
        failure{
            script {
                bat ''' curl -s -X POST https://api.telegram.org/bot"%TELEGRAM_TOKEN%"/sendMessage -d chat_id="%TELEGRAM_CHAT_ID%" -d text="%TEXT_FAILURE_BUILD%" '''
            }
        }
        aborted{
            script {
                bat ''' curl -s -X POST https://api.telegram.org/bot"%TELEGRAM_TOKEN%"/sendMessage -d chat_id="%TELEGRAM_CHAT_ID%" -d text="%TEXT_ABORTED_BUILD%" '''
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A little explanation of my JenkinsFile:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I set environment that I need to run the pipeline like the credentials that will be used.&lt;/li&gt;
&lt;li&gt;To make &lt;code&gt;mvn&lt;/code&gt; command works, I have to define &lt;code&gt;MAVEN_HOME&lt;/code&gt; that is already configured in my Jenkins environment.&lt;/li&gt;
&lt;li&gt;The first stage of the pipeline is called &lt;code&gt;Pre-Build&lt;/code&gt; where a chat will be sent to your telegram account that Jenkins is building something.&lt;/li&gt;
&lt;li&gt;In the second stage of build I use a command of &lt;code&gt;mvn clean install&lt;/code&gt; since I have to run SonarQube and SonarQube is running with the package generated by that command.&lt;/li&gt;
&lt;li&gt;The third stage &lt;code&gt;Sonarqube analysis&lt;/code&gt; is using SonarQube environment that I have already configured like the token credential and the installation name. Jenkins will run the command of &lt;code&gt;mvn sonar:sonar&lt;/code&gt; to analyze the code.&lt;/li&gt;
&lt;li&gt;The last stage is to tell the Jenkins build status based on the result of SonarQube analysis. Either it is success, fail, or aborted.&lt;/li&gt;
&lt;li&gt;After these stages is run, Jenkins will send another chat to let you know the build result.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Makesure you push the JenkinsFile to your Git code base.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating Jenkins Pipeline Project
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Open Jenkins Dashboard.&lt;/li&gt;
&lt;li&gt;At sidebar, choose &lt;code&gt;New Item&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Enter your project name.&lt;/li&gt;
&lt;li&gt;Choose &lt;code&gt;Pipeline&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Click &lt;code&gt;OK&lt;/code&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0bz4ha6iutva9ioj9jr8.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;When you already get redirected, tick the &lt;code&gt;Github project&lt;/code&gt; checkbox.&lt;/li&gt;
&lt;li&gt;Enter the repository url.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgebqepjsrfw1rwibo2bu.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Scroll down until you get into &lt;code&gt;Pipeline&lt;/code&gt; Section.&lt;/li&gt;
&lt;li&gt;In &lt;code&gt;Definition&lt;/code&gt; dropdown, choose &lt;code&gt;Pipeline script from SCM&lt;/code&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnsr9l37iwt6e7pd9mf1z.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;In &lt;code&gt;SCM&lt;/code&gt; dropdown, choose &lt;code&gt;Git&lt;/code&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnivr6fz14jeyplfvv2do.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Enter your URL repository that you usually use to clone the project.&lt;/li&gt;
&lt;li&gt;Enter the Git credentials. If you don't have it yet, you can just adding them by choosing username and password as the &lt;code&gt;Kind&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Change the &lt;code&gt;Branches&lt;/code&gt; to build if needed. By default it is &lt;code&gt;master&lt;/code&gt; but I change mine to &lt;code&gt;main&lt;/code&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpmldr6dik4zwu3fk9try.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;On &lt;code&gt;Script Path&lt;/code&gt; field, enter the jenkins file name. I think it is case sensitive so make sure it is correct. Either it is &lt;code&gt;JenkinsFile&lt;/code&gt; or &lt;code&gt;Jenkinsfile&lt;/code&gt; or other.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy1uz4gi61vxx6o6nvsxv.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;code&gt;Save&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Building the project
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;At Jenkins Dashboard, choose the project you just created.&lt;/li&gt;
&lt;li&gt;At the sidebar, choose &lt;code&gt;Build Now&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Wait until the build is finish.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;On the start of the build you will get chat at Telegram like this:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm02pryex9lpdnuqswwmh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm02pryex9lpdnuqswwmh.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After Build finish you will get a chat like this:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8poha5piebzym7gpijqr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8poha5piebzym7gpijqr.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is my chat lookslike after several builds (this is the bot that I created at the first try that's why it is different).&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpvzchw0n5tqvddgsxdsi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpvzchw0n5tqvddgsxdsi.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>jenkins</category>
      <category>beginners</category>
      <category>codenewbie</category>
    </item>
    <item>
      <title>How to: SonarQube Code Coverage in Jenkins with Maven Project</title>
      <dc:creator>Amalia Hajarani</dc:creator>
      <pubDate>Wed, 22 Nov 2023 09:48:10 +0000</pubDate>
      <link>https://dev.to/amalja0/how-to-sonarqube-code-coverage-in-jenkins-with-maven-project-3bb6</link>
      <guid>https://dev.to/amalja0/how-to-sonarqube-code-coverage-in-jenkins-with-maven-project-3bb6</guid>
      <description>&lt;p&gt;Hallo dear everyone whoever read this post. Today I'd love to share my learning journey with Jenkins. In today's post I will be using  &lt;a href="https://dev.to/amalja0/lokomotif-use-case-4-implementing-spring-boot-scheduler-report-1h6l"&gt;this code&lt;/a&gt; as my use case and I'll be cooking from scratch which is installing Jenkins until we have the final dish of seeing the code coverage of our maven project in SonarQube. &lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisite
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Installed Java version of 17 and have it configured in environment variable&lt;/li&gt;
&lt;li&gt;Installed Maven version of 3.9.5 and have it configured in environment variable&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Installing Jenkins
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Since I'm on Windows 10, I downloaded Jenkins lts version (mine is 2.426.1 LTS) from this &lt;a href="https://www.jenkins.io/download/#downloading-jenkins" rel="noopener noreferrer"&gt;official link&lt;/a&gt;. &lt;/li&gt;
&lt;li&gt;Open the installer and click &lt;code&gt;Next&lt;/code&gt;. 
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ubniz5rkj42fvpg6h3k.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;I'm going to use the default path so I just click &lt;code&gt;Next&lt;/code&gt;. 
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgjvqvlwoxh40tv32hc8l.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Since I cannot remember my own credential, I choose the &lt;code&gt;Run service as LocalSystem&lt;/code&gt;. Then, click &lt;code&gt;Next&lt;/code&gt;. 
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg5xks1bd6qoem83czbyg.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;I choose the default port which is 8085. Click Test Port then proceed to &lt;code&gt;Next&lt;/code&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ksgumry5i2cgb7cjawk.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Choose your java version. Mine is 17. If the path is correct, just click &lt;code&gt;Next&lt;/code&gt;. 
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn9uk777zefrhuhltar3z.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;I don't understand what is happening here, so I assume it is safe to just click &lt;code&gt;Next&lt;/code&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fajrlvxuo62hyepfn1l86.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;And now Jenkins is ready to be installed! Don't forget to click the &lt;code&gt;Install&lt;/code&gt; button. After that you might have to wait for a few times until installation is finish.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi43zu9tvmim2o68nibyg.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Now that installation is finished already, we can move on by clicking &lt;code&gt;Finish&lt;/code&gt; button.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7nkg3ebdmv3k8m49q56c.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Installing and Prepare SonarQube
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;I downloaded the latest version of SonarQube from &lt;a href="https://www.sonarsource.com/products/sonarqube/downloads/" rel="noopener noreferrer"&gt;this official link&lt;/a&gt;. The version is now at Version 10.3.&lt;/li&gt;
&lt;li&gt;Unzip the file in your dedicated folder. Mine is on &lt;code&gt;D: drive&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;We might cannot start the SonarQube just now because we have to do thing to make sure the configuration is working.&lt;/li&gt;
&lt;li&gt;To make things easier, open &lt;code&gt;sonar.properties&lt;/code&gt; in code editor. You can find the file by entering the unzip directory result, go to &lt;code&gt;conf&lt;/code&gt; directory, and there laying the &lt;code&gt;sonar.properties&lt;/code&gt;. Now I uncomment few things in that file such as below lines:
&lt;code&gt;
sonar.web.host=0.0.0.0
sonar.web.port=9000
sonar.search.port=0 // For this line you might want to change the port as well. Mine is from `9001` to `0`
&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Don't forget to save after making those changes.&lt;/li&gt;
&lt;li&gt;Now let's try to run SonarQube server by going to &lt;code&gt;bin&lt;/code&gt; directory, it is at the same level as &lt;code&gt;conf&lt;/code&gt; directory. Choose the directory that match your operating system. For me, I choose &lt;code&gt;windows-x86-64&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Open &lt;code&gt;command prompt&lt;/code&gt; from that directory.&lt;/li&gt;
&lt;li&gt;Run this command:
&lt;code&gt;
StartSonar.bat
&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;If it's working properly this is what will you see at the bottom of command line:
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqh51eyz8g99ey4ejowvt.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Updating POM.xml
&lt;/h2&gt;

&lt;p&gt;You can find the original resource at &lt;a href="https://www.baeldung.com/sonarqube-jacoco-code-coverage" rel="noopener noreferrer"&gt;here&lt;/a&gt; for this step. But it wasn't working at the first try since some of the versions are not compatible with my java version. But the final &lt;code&gt;POM.xml&lt;/code&gt; looks like:&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;br&gt;
&amp;lt;project xmlns="&lt;a href="http://maven.apache.org/POM/4.0.0" rel="noopener noreferrer"&gt;http://maven.apache.org/POM/4.0.0&lt;/a&gt;" xmlns:xsi="&lt;a href="http://www.w3.org/2001/XMLSchema-instance" rel="noopener noreferrer"&gt;http://www.w3.org/2001/XMLSchema-instance&lt;/a&gt;"&lt;br&gt;
    xsi:schemaLocation="&lt;a href="http://maven.apache.org/POM/4.0.0" rel="noopener noreferrer"&gt;http://maven.apache.org/POM/4.0.0&lt;/a&gt; &lt;a href="https://maven.apache.org/xsd/maven-4.0.0.xsd" rel="noopener noreferrer"&gt;https://maven.apache.org/xsd/maven-4.0.0.xsd&lt;/a&gt;"&amp;gt;&lt;br&gt;
    &amp;lt;modelVersion&amp;gt;4.0.0&amp;lt;/modelVersion&amp;gt;&lt;br&gt;
    &amp;lt;parent&amp;gt;&lt;br&gt;
        &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;&lt;br&gt;
        &amp;lt;artifactId&amp;gt;spring-boot-starter-parent&amp;lt;/artifactId&amp;gt;&lt;br&gt;
        &amp;lt;version&amp;gt;3.0.0&amp;lt;/version&amp;gt;&lt;br&gt;
        &amp;lt;relativePath/&amp;gt; &amp;lt;!-- lookup parent from repository --&amp;gt;&lt;br&gt;
    &amp;lt;/parent&amp;gt;&lt;br&gt;
    &amp;lt;groupId&amp;gt;com.tujuhsembilan&amp;lt;/groupId&amp;gt;&lt;br&gt;
    &amp;lt;artifactId&amp;gt;scheduler&amp;lt;/artifactId&amp;gt;&lt;br&gt;
    &amp;lt;version&amp;gt;0.0.1-SNAPSHOT&amp;lt;/version&amp;gt;&lt;br&gt;
    &amp;lt;name&amp;gt;scheduler&amp;lt;/name&amp;gt;&lt;br&gt;
    &amp;lt;description&amp;gt;Demo project for Spring Boot&amp;lt;/description&amp;gt;&lt;br&gt;
    &amp;lt;properties&amp;gt;&lt;br&gt;
        &amp;lt;java.version&amp;gt;17&amp;lt;/java.version&amp;gt;&lt;br&gt;
        &amp;lt;jacoco.version&amp;gt;0.8.8&amp;lt;/jacoco.version&amp;gt;&lt;br&gt;
        &amp;lt;sonar.java.coveragePlugin&amp;gt;jacoco&amp;lt;/sonar.java.coveragePlugin&amp;gt;&lt;br&gt;
        &amp;lt;sonar.dynamicAnalysis&amp;gt;reuseReports&amp;lt;/sonar.dynamicAnalysis&amp;gt;&lt;br&gt;
        &amp;lt;sonar.jacoco.reportPath&amp;gt;${project.basedir}/../target/jacoco.exec&amp;lt;/sonar.jacoco.reportPath&amp;gt;&lt;br&gt;
        &amp;lt;sonar.language&amp;gt;java&amp;lt;/sonar.language&amp;gt;&lt;br&gt;
    &amp;lt;/properties&amp;gt;&lt;br&gt;
    &amp;lt;dependencies&amp;gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;amp;lt;dependency&amp;amp;gt;
        &amp;amp;lt;groupId&amp;amp;gt;org.springframework.boot&amp;amp;lt;/groupId&amp;amp;gt;
        &amp;amp;lt;artifactId&amp;amp;gt;spring-boot-starter-data-jpa&amp;amp;lt;/artifactId&amp;amp;gt;
    &amp;amp;lt;/dependency&amp;amp;gt;

    &amp;amp;lt;dependency&amp;amp;gt;
        &amp;amp;lt;groupId&amp;amp;gt;org.springframework.boot&amp;amp;lt;/groupId&amp;amp;gt;
        &amp;amp;lt;artifactId&amp;amp;gt;spring-boot-starter-hateoas&amp;amp;lt;/artifactId&amp;amp;gt;
    &amp;amp;lt;/dependency&amp;amp;gt;

    &amp;amp;lt;dependency&amp;amp;gt;
        &amp;amp;lt;groupId&amp;amp;gt;org.springframework.boot&amp;amp;lt;/groupId&amp;amp;gt;
        &amp;amp;lt;artifactId&amp;amp;gt;spring-boot-starter-quartz&amp;amp;lt;/artifactId&amp;amp;gt;
    &amp;amp;lt;/dependency&amp;amp;gt;

    &amp;amp;lt;dependency&amp;amp;gt;
        &amp;amp;lt;groupId&amp;amp;gt;com.h2database&amp;amp;lt;/groupId&amp;amp;gt;
        &amp;amp;lt;artifactId&amp;amp;gt;h2&amp;amp;lt;/artifactId&amp;amp;gt;
        &amp;amp;lt;scope&amp;amp;gt;runtime&amp;amp;lt;/scope&amp;amp;gt;
    &amp;amp;lt;/dependency&amp;amp;gt;

    &amp;amp;lt;dependency&amp;amp;gt;
        &amp;amp;lt;groupId&amp;amp;gt;org.projectlombok&amp;amp;lt;/groupId&amp;amp;gt;
        &amp;amp;lt;artifactId&amp;amp;gt;lombok&amp;amp;lt;/artifactId&amp;amp;gt;
        &amp;amp;lt;optional&amp;amp;gt;true&amp;amp;lt;/optional&amp;amp;gt;
    &amp;amp;lt;/dependency&amp;amp;gt;

    &amp;amp;lt;dependency&amp;amp;gt;
        &amp;amp;lt;groupId&amp;amp;gt;org.springframework.boot&amp;amp;lt;/groupId&amp;amp;gt;
        &amp;amp;lt;artifactId&amp;amp;gt;spring-boot-starter-test&amp;amp;lt;/artifactId&amp;amp;gt;
        &amp;amp;lt;scope&amp;amp;gt;test&amp;amp;lt;/scope&amp;amp;gt;
    &amp;amp;lt;/dependency&amp;amp;gt;

    &amp;amp;lt;dependency&amp;amp;gt;
        &amp;amp;lt;groupId&amp;amp;gt;org.springframework.boot&amp;amp;lt;/groupId&amp;amp;gt;
        &amp;amp;lt;artifactId&amp;amp;gt;spring-boot-starter-data-mongodb&amp;amp;lt;/artifactId&amp;amp;gt;
    &amp;amp;lt;/dependency&amp;amp;gt;

    &amp;amp;lt;dependency&amp;amp;gt;
        &amp;amp;lt;groupId&amp;amp;gt;org.modelmapper&amp;amp;lt;/groupId&amp;amp;gt;
        &amp;amp;lt;artifactId&amp;amp;gt;modelmapper&amp;amp;lt;/artifactId&amp;amp;gt;
        &amp;amp;lt;version&amp;amp;gt;3.0.0&amp;amp;lt;/version&amp;amp;gt;
    &amp;amp;lt;/dependency&amp;amp;gt;

    &amp;amp;lt;dependency&amp;amp;gt;
        &amp;amp;lt;groupId&amp;amp;gt;org.springframework.boot&amp;amp;lt;/groupId&amp;amp;gt;
        &amp;amp;lt;artifactId&amp;amp;gt;spring-boot-starter-web&amp;amp;lt;/artifactId&amp;amp;gt;
    &amp;amp;lt;/dependency&amp;amp;gt;

    &amp;amp;lt;dependency&amp;amp;gt;
        &amp;amp;lt;groupId&amp;amp;gt;org.springframework.boot&amp;amp;lt;/groupId&amp;amp;gt;
        &amp;amp;lt;artifactId&amp;amp;gt;spring-boot-starter-data-rest&amp;amp;lt;/artifactId&amp;amp;gt;
    &amp;amp;lt;/dependency&amp;amp;gt;

    &amp;amp;lt;dependency&amp;amp;gt;
        &amp;amp;lt;groupId&amp;amp;gt;org.postgresql&amp;amp;lt;/groupId&amp;amp;gt;
        &amp;amp;lt;artifactId&amp;amp;gt;postgresql&amp;amp;lt;/artifactId&amp;amp;gt;
        &amp;amp;lt;scope&amp;amp;gt;runtime&amp;amp;lt;/scope&amp;amp;gt;
    &amp;amp;lt;/dependency&amp;amp;gt;

    &amp;amp;lt;dependency&amp;amp;gt;
        &amp;amp;lt;groupId&amp;amp;gt;org.telegram&amp;amp;lt;/groupId&amp;amp;gt;
        &amp;amp;lt;artifactId&amp;amp;gt;telegrambots-spring-boot-starter&amp;amp;lt;/artifactId&amp;amp;gt;
        &amp;amp;lt;version&amp;amp;gt;6.1.0&amp;amp;lt;/version&amp;amp;gt;
    &amp;amp;lt;/dependency&amp;amp;gt;
    &amp;amp;lt;dependency&amp;amp;gt;
        &amp;amp;lt;groupId&amp;amp;gt;org.telegram&amp;amp;lt;/groupId&amp;amp;gt;
        &amp;amp;lt;artifactId&amp;amp;gt;telegrambots-abilities&amp;amp;lt;/artifactId&amp;amp;gt;
        &amp;amp;lt;version&amp;amp;gt;6.7.0&amp;amp;lt;/version&amp;amp;gt;
    &amp;amp;lt;/dependency&amp;amp;gt;

    &amp;amp;lt;dependency&amp;amp;gt;
        &amp;amp;lt;groupId&amp;amp;gt;org.springframework.boot&amp;amp;lt;/groupId&amp;amp;gt;
        &amp;amp;lt;artifactId&amp;amp;gt;spring-boot-starter-security&amp;amp;lt;/artifactId&amp;amp;gt;
    &amp;amp;lt;/dependency&amp;amp;gt;

    &amp;amp;lt;dependency&amp;amp;gt;
        &amp;amp;lt;groupId&amp;amp;gt;org.jacoco&amp;amp;lt;/groupId&amp;amp;gt; 
        &amp;amp;lt;artifactId&amp;amp;gt;jacoco-maven-plugin&amp;amp;lt;/artifactId&amp;amp;gt;
        &amp;amp;lt;version&amp;amp;gt;0.8.6&amp;amp;lt;/version&amp;amp;gt;
    &amp;amp;lt;/dependency&amp;amp;gt;

&amp;amp;lt;/dependencies&amp;amp;gt;

&amp;amp;lt;build&amp;amp;gt;
    &amp;amp;lt;plugins&amp;amp;gt;
        &amp;amp;lt;plugin&amp;amp;gt;
            &amp;amp;lt;groupId&amp;amp;gt;org.springframework.boot&amp;amp;lt;/groupId&amp;amp;gt;
            &amp;amp;lt;artifactId&amp;amp;gt;spring-boot-maven-plugin&amp;amp;lt;/artifactId&amp;amp;gt;
        &amp;amp;lt;/plugin&amp;amp;gt;
        &amp;amp;lt;plugin&amp;amp;gt;
            &amp;amp;lt;groupId&amp;amp;gt;org.jacoco&amp;amp;lt;/groupId&amp;amp;gt;
            &amp;amp;lt;artifactId&amp;amp;gt;jacoco-maven-plugin&amp;amp;lt;/artifactId&amp;amp;gt;
            &amp;amp;lt;version&amp;amp;gt;${jacoco.version}&amp;amp;lt;/version&amp;amp;gt;
            &amp;amp;lt;executions&amp;amp;gt;
                &amp;amp;lt;execution&amp;amp;gt;
                    &amp;amp;lt;id&amp;amp;gt;jacoco-initialize&amp;amp;lt;/id&amp;amp;gt;
                    &amp;amp;lt;goals&amp;amp;gt;
                        &amp;amp;lt;goal&amp;amp;gt;prepare-agent&amp;amp;lt;/goal&amp;amp;gt;
                    &amp;amp;lt;/goals&amp;amp;gt;
                &amp;amp;lt;/execution&amp;amp;gt;
                &amp;amp;lt;execution&amp;amp;gt;
                    &amp;amp;lt;id&amp;amp;gt;jacoco-site&amp;amp;lt;/id&amp;amp;gt;
                    &amp;amp;lt;phase&amp;amp;gt;package&amp;amp;lt;/phase&amp;amp;gt;
                    &amp;amp;lt;goals&amp;amp;gt;
                        &amp;amp;lt;goal&amp;amp;gt;report&amp;amp;lt;/goal&amp;amp;gt;
                    &amp;amp;lt;/goals&amp;amp;gt;
                &amp;amp;lt;/execution&amp;amp;gt;
            &amp;amp;lt;/executions&amp;amp;gt;
        &amp;amp;lt;/plugin&amp;amp;gt;
    &amp;amp;lt;/plugins&amp;amp;gt;
&amp;amp;lt;/build&amp;amp;gt;
&amp;amp;lt;repositories&amp;amp;gt;
    &amp;amp;lt;repository&amp;amp;gt;
        &amp;amp;lt;id&amp;amp;gt;spring-releases&amp;amp;lt;/id&amp;amp;gt;
        &amp;amp;lt;name&amp;amp;gt;Spring Releases&amp;amp;lt;/name&amp;amp;gt;
        &amp;amp;lt;url&amp;amp;gt;https://repo.spring.io/libs-release&amp;amp;lt;/url&amp;amp;gt;
    &amp;amp;lt;/repository&amp;amp;gt;
&amp;amp;lt;/repositories&amp;amp;gt;
&amp;amp;lt;pluginRepositories&amp;amp;gt;
    &amp;amp;lt;pluginRepository&amp;amp;gt;
        &amp;amp;lt;id&amp;amp;gt;spring-releases&amp;amp;lt;/id&amp;amp;gt;
        &amp;amp;lt;name&amp;amp;gt;Spring Releases&amp;amp;lt;/name&amp;amp;gt;
        &amp;amp;lt;url&amp;amp;gt;https://repo.spring.io/libs-release&amp;amp;lt;/url&amp;amp;gt;
    &amp;amp;lt;/pluginRepository&amp;amp;gt;
&amp;amp;lt;/pluginRepositories&amp;amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&amp;lt;/project&amp;gt;&lt;br&gt;
&lt;/p&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Running SonarQube in Local Environment&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;Now that I have the SonarQube and the project ready, I tried to run it, but some problem happened related to my maven settings. I hope you're not going to have the problem, but this is how I update my maven settings (you can find the settings at MAVEN_HOME/conf):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Adding sonarscanner plugin group.&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;pluginGroup&amp;gt;org.sonarsource.scanner.maven&amp;lt;/pluginGroup&amp;gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Adding sonar profile.&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;profile&amp;gt;
        &amp;lt;id&amp;gt;sonar&amp;lt;/id&amp;gt;
        &amp;lt;activation&amp;gt;
            &amp;lt;activeByDefault&amp;gt;true&amp;lt;/activeByDefault&amp;gt;
        &amp;lt;/activation&amp;gt;
        &amp;lt;properties&amp;gt;
            &amp;lt;!-- Optional URL to server. Default value is http://localhost:9000 --&amp;gt;
            &amp;lt;sonar.host.url&amp;gt;
              http://localhost:9000
            &amp;lt;/sonar.host.url&amp;gt;
        &amp;lt;/properties&amp;gt;
    &amp;lt;/profile&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, let's try to run SonarQube locally using command prompt&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;code&gt;command prompt&lt;/code&gt; from the root of the project and run:
&lt;code&gt;
mvn clean install
&lt;/code&gt;
If the build success you will have this on your command prompt:
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzvr7ct59hzwiwgyniaj0.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Start the SonarQube server&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Get back to the project command line and run:&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mvn sonar:sonar -Dsonar.login=admin -Dsonar.password=admin
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I did not change any credential, so by default the credential should be as what written above.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzie1wk2e50t8pemtf9q4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzie1wk2e50t8pemtf9q4.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If the build succeed, you might wanna see some previous line to know where you can open the coverage result. Mine is at &lt;code&gt;http://localhost:9000/dashboard?id=com.tujuhsembilan%3Ascheduler&lt;/code&gt;. Disclaimer: you might need to login for the first time using the default credentials and have to change your password, but after that you should be good to go.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcy6tj4y4sa4jda8r0xgg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcy6tj4y4sa4jda8r0xgg.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Creating Jenkins Maven Project and Implementing SonarQube
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Configuring Jenkins for the first time
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;code&gt;localhost:8085&lt;/code&gt;. Jenkins is automatically start by default. From here, I will use the picture from the &lt;a href="https://www.jenkins.io/doc/book/installing/windows/" rel="noopener noreferrer"&gt;official site&lt;/a&gt; since mine is already unlcoked. You will have to unlock jenkins by inputing the password provided in red block. Then click continue.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5ll9dtc6fsld6k4b7i2e.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;You will be asked to install some plugins. I choose the &lt;code&gt;Install suggested plugins&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Now you will be redirected to creating admin user. Just fill it as you wish and then click &lt;code&gt;Save and Continue&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;After that put &lt;code&gt;http://localhost:8085/&lt;/code&gt; as Jenkins URL and click &lt;code&gt;Save and Finish&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;You are now supposed to be at the dashboard.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Configuring SonarQube Scanner Plugin in Jenkins
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;In the Dashboard, click &lt;code&gt;Manage Jenkins&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Choose &lt;code&gt;Plugins&lt;/code&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftq2zmj7mtbe38l9bi33z.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Choose Available Plugin and search &lt;code&gt;SonarQube Scanner for Jenkins&lt;/code&gt;. Tick the checkbox and click &lt;code&gt;Install&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Configuring Tools
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;In the Dashboard, click &lt;code&gt;Manage Jenkins&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Choose &lt;code&gt;Tools&lt;/code&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft5dj6gxe2xp5cljr9ni0.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Add JDK installations and fill it with your &lt;code&gt;JAVA_HOME&lt;/code&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F279ugjtt9ehri0efsdkp.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Add Maven installations and fill it with your &lt;code&gt;MAVEN_HOME&lt;/code&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foyynxwl2vyys8a7meb82.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Getting SonarQube Token
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Make sure that SonarQube server is still running.&lt;/li&gt;
&lt;li&gt;Go to &lt;code&gt;http://localhost:9000/&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;On the right corner, click on your initial and choose &lt;code&gt;My Account&lt;/code&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2c1yoctawpcnfvdq2i38.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2c1yoctawpcnfvdq2i38.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Go to &lt;code&gt;Security&lt;/code&gt; tab.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcsnnunrz2famjn8nv90i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcsnnunrz2famjn8nv90i.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fill the field and click &lt;code&gt;Generate&lt;/code&gt;.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdj2s7qqp2kh09xyiij5h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdj2s7qqp2kh09xyiij5h.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Copy the token to somewhere save because we are not able to see it again.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Configuring System
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;In the Dashboard, click &lt;code&gt;Manage Jenkins&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Choose &lt;code&gt;System&lt;/code&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjiih8ggyyyrh7p6wdhi4.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Go to &lt;code&gt;SonarQube servers&lt;/code&gt;. Tick the check box for &lt;code&gt;Environment variables&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;SonarQube installations&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Fill the name and Server URL
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp7v4p8qiazzaxyvy72nt.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;In Server authentication token field, click &lt;code&gt;Add&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Choose &lt;code&gt;Jenkins&lt;/code&gt;. &lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;Kind&lt;/code&gt; dropdown, choose &lt;code&gt;Secret text&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Leave the scope.&lt;/li&gt;
&lt;li&gt;Enter the SonarQube secret token that you copy before.&lt;/li&gt;
&lt;li&gt;Fill the &lt;code&gt;ID&lt;/code&gt; with some name that make it easier to know what credential is that for.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs3x6ht6vqapv100mohwn.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;code&gt;Add&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Back to &lt;code&gt;Server authentication token&lt;/code&gt; field, click the dropdown and choose the newly generated credential.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvw3grheedfyser6s3xxl.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;code&gt;Save&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Creating Maven Project
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;In the dashboard, click &lt;code&gt;New Item&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Enter the project name and choose Maven Project. Then Click &lt;code&gt;OK&lt;/code&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2x4aqv6avbbla26qut0j.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Now we are going to configure build settings.&lt;/li&gt;
&lt;li&gt;In &lt;code&gt;Source Code Management&lt;/code&gt; section, choose &lt;code&gt;Git&lt;/code&gt;. Make sure you alredy push the latest change especially the &lt;code&gt;POM.xml&lt;/code&gt; change to your repository.&lt;/li&gt;
&lt;li&gt;Enter your repository URL that you usually use to clone the project.&lt;/li&gt;
&lt;li&gt;In &lt;code&gt;credential&lt;/code&gt; fields, click &lt;code&gt;Add&lt;/code&gt;, choose &lt;code&gt;Jenkins&lt;/code&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqvvc381ad6lzk2qimfsr.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;In &lt;code&gt;Kind&lt;/code&gt; dropdown, choose &lt;code&gt;Username with password&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Leave the Scope.&lt;/li&gt;
&lt;li&gt;Enter your github username.&lt;/li&gt;
&lt;li&gt;Enter your github password.&lt;/li&gt;
&lt;li&gt;Again, fill the ID with some name that make it easier to know what credential is that for. 
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsyvnj46mwcsc3v7tv2kh.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;click &lt;code&gt;Add&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Back to &lt;code&gt;credential&lt;/code&gt; field dropdown, choose your github credential.&lt;/li&gt;
&lt;li&gt;Moving to &lt;code&gt;Build Environment&lt;/code&gt; section, tick the &lt;code&gt;Prepare SonarQube Scanner environment&lt;/code&gt;, and again in the dropdown, choose the SonarQube token credential.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcnmkc89lgtnyk34wm791.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Moving to the  &lt;code&gt;Build&lt;/code&gt; section you want to add this to the &lt;code&gt;Goals and options&lt;/code&gt;.
&lt;code&gt;
clean install $SONAR_MAVEN_GOAL
&lt;/code&gt;
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw80ccdo9zvj6ppkhl7cw.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;code&gt;Save&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Building Maven Project
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;From Dashboard, you can see your new project. Click on the project.&lt;/li&gt;
&lt;li&gt;In the sidebar you can see &lt;code&gt;Build Now&lt;/code&gt; button. Click that.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F163t1u4mvrkumxdza5mq.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Wait for the build.&lt;/li&gt;
&lt;li&gt;When the build is success, you can click &lt;code&gt;SonarQube&lt;/code&gt; button in sidebar.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft4wjyjdvvzqizze54nco.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Now you have your code coverage running from Jenkins!
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsajdlhbtduvurjii4zdd.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The working repository can be seen &lt;a href="https://github.com/amalja0/jenkins-lokomotif-scheduler-report" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>jenkins</category>
      <category>maven</category>
      <category>sonarqube</category>
    </item>
    <item>
      <title>Lokomotif: Use Case (5: Implementing Vite-React)</title>
      <dc:creator>Amalia Hajarani</dc:creator>
      <pubDate>Wed, 22 Nov 2023 01:53:23 +0000</pubDate>
      <link>https://dev.to/amalja0/lokomotif-use-case-5-implementing-vite-react-3ec8</link>
      <guid>https://dev.to/amalja0/lokomotif-use-case-5-implementing-vite-react-3ec8</guid>
      <description>&lt;p&gt;Prerequisites:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Node version of 16.20.2&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And now, let's go into the cooking step:&lt;/p&gt;

&lt;h2&gt;
  
  
  Initialize Node application
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Create a directory dedicated for this project. For me the name is front-end-loko-kai&lt;/li&gt;
&lt;li&gt;Open command prompt from that directory&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run this command:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm create vite .
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Choose the framework by using up and down arrow keys, I choose React. Then hit enter&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Choose the programming language, I use plain JavaScript, then hit enter&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Et voila, your application boilerplate is done.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Install npm packages
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Installing &lt;code&gt;mui&lt;/code&gt;. This package will be use as css framework&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install @mui/material @emotion/react @emotion/styled
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Installing &lt;code&gt;axios&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install axios
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Installing chart dependencies&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install chart.js react-chartjs-2
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Creating .env
&lt;/h2&gt;

&lt;p&gt;This is how my &lt;code&gt;.env&lt;/code&gt; lookslike:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;VITE_BASE_URL=http://localhost:8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating project directory structure
&lt;/h2&gt;

&lt;p&gt;As you get boilerplate, you might want to delete some of it because of its irrelevancy. Below is project structure that I use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;*src
    - assets
    - components
    - services
    - utils
    - views
    - App.jsx
    - index.css
    - main.jsx
    - theme.js
*.env
*package.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating global theme
&lt;/h2&gt;

&lt;p&gt;I want to use Roboto font instead the built in one and make sure that my typography is responsive. So in &lt;code&gt;theme.js&lt;/code&gt; I put these lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { createTheme, responsiveFontSizes } from '@mui/material/styles';

let theme = createTheme({
    typography: {
        fontFamily: [
            'Roboto', 
            'sans-serif'
        ].join(',')
    }
});

theme = responsiveFontSizes(theme);

export default theme;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To apply the theme in your application, you can update your &lt;code&gt;App.jsx&lt;/code&gt; like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { ThemeProvider } from '@mui/material'
import theme from './theme'
import Dashboard from './views/Dashboard'

function App() {

  return (
    &amp;lt;ThemeProvider theme={theme}&amp;gt;
      &amp;lt;Dashboard/&amp;gt;
    &amp;lt;/ThemeProvider&amp;gt;
  )
}

export default App
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Don't worry about &lt;code&gt;Dashboard&lt;/code&gt; component, we will be there soon. Then, if you want more independency of styling the application you might wanna update &lt;code&gt;index.css&lt;/code&gt;. Here's mine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:root {
  font-family: 'Roboto', sans-serif;
  line-height: 1.5;
  background: linear-gradient(90deg, rgba(253,202,198,1) 0%, rgba(252,248,223,1) 100%);

  font-synthesis: none;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating views
&lt;/h2&gt;

&lt;p&gt;I only make a very simple dashboard. Hence, I only have one view which is dashboard.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In &lt;code&gt;view&lt;/code&gt; directory, create a new directory called &lt;code&gt;Dashboard&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Inside &lt;code&gt;Dasboard&lt;/code&gt; directory, create a file called &lt;code&gt;index.jsx&lt;/code&gt;. This is the content of the file:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Container, Grid, Typography } from '@mui/material';
import React from 'react'
import Header from '../../components/Header';
import ReportSummary from '../../components/ReportSummary';
import StatusDescription from '../../components/StatusDescription';

const centerContentStyles = {
  width: '100%',
  display: 'flex',
  alignItems: 'center',
  justifyContent: 'center',
};

function Dashboard() {
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;Container &amp;gt;
        &amp;lt;Grid spacing={3} container direction={"column"} alignItems={"center"} justifyContent={"center"} sx={{ height: '97vh' }}&amp;gt;
          &amp;lt;Grid item md={2}  sx={ centerContentStyles }&amp;gt;
            &amp;lt;Header/&amp;gt;
          &amp;lt;/Grid&amp;gt;
          &amp;lt;Grid item md={4} sx={ centerContentStyles }&amp;gt;
            &amp;lt;Grid spacing={4} container sx={{height: '100%'}}&amp;gt;
              &amp;lt;Grid item md={5}&amp;gt;
                &amp;lt;StatusDescription /&amp;gt;
              &amp;lt;/Grid&amp;gt;
              &amp;lt;Grid item md={7}&amp;gt;
                &amp;lt;ReportSummary /&amp;gt;
              &amp;lt;/Grid&amp;gt;
            &amp;lt;/Grid&amp;gt;
          &amp;lt;/Grid&amp;gt;
        &amp;lt;/Grid&amp;gt;
      &amp;lt;/Container&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}

export default Dashboard;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Creating Header component
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;In &lt;code&gt;components&lt;/code&gt; directory, create a new directory called &lt;code&gt;Header&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Inside &lt;code&gt;Header&lt;/code&gt; directory, create a file called &lt;code&gt;index.jsx&lt;/code&gt;. This is the content of the file:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Container, Grid, Typography } from '@mui/material';
import React from 'react'

function Header() {
  return (
    &amp;lt;div style={{ 
      display:'flex', 
      alignItems: 'center',  
      width: '100%', 
      height: '70%', 
      backgroundColor: '#FDF2EF',
      borderRadius: '1rem' 
    }}&amp;gt;
      &amp;lt;Container sx={{ textAlign: 'center' }}&amp;gt;
        &amp;lt;Typography variant='h4' fontWeight={"bold"} sx={{ color: '#ad917f' }}&amp;gt;Lokomotif Dashboard&amp;lt;/Typography&amp;gt;
      &amp;lt;/Container&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}

export default Header;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Creating Status Description component
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;In &lt;code&gt;components&lt;/code&gt; directory, create a new directory called &lt;code&gt;StatusDescription&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Inside &lt;code&gt;StatusDescription&lt;/code&gt; directory, create a file called &lt;code&gt;index.jsx&lt;/code&gt;. This is the content of the file:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Grid, Typography } from '@mui/material';
import React from 'react';
import { statusDescription } from '../../utils/Constants';

function StatusDescription() {
  const detailedStatusDescription = statusDescription;

  return (
    &amp;lt;Grid 
      container 
      gap={2}
      sx={{
        display:'flex', 
        flexDirection: 'column',
        alignItems: 'left',  
        width: '100%', 
        height: '100%', 
        backgroundColor: '#FDF2EF',
        borderRadius: '1rem',
        padding: '1rem' 
      }}
    &amp;gt; 
      &amp;lt;Grid item&amp;gt;
        &amp;lt;Typography variant='h6' fontWeight={"bold"} sx={{ color: '#855ef5' }}&amp;gt;Lokomotif Status Description&amp;lt;/Typography&amp;gt;
      &amp;lt;/Grid&amp;gt;
      &amp;lt;Grid item&amp;gt;
        {
          detailedStatusDescription.map((status) =&amp;gt; {
            return (
              &amp;lt;Typography sx={{ marginBottom: '0.25rem' }}&amp;gt;
                Status of &amp;lt;span style={{ color: '#855ef5', fontWeight:'bold' }}&amp;gt;{status.status}&amp;lt;/span&amp;gt;: {status.description}
              &amp;lt;/Typography&amp;gt;
            )
          })
        }
      &amp;lt;/Grid&amp;gt;
    &amp;lt;/Grid&amp;gt;
  )
}

export default StatusDescription;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a JSON containing status locomotive and its description by adding new file called &lt;code&gt;Constants.js&lt;/code&gt; inside &lt;code&gt;utils&lt;/code&gt; folder&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const statusDescription = [
    {
        status: 1,
        description: "New Lokomotif"
    },
    {
        status: 2,
        description: "In Use Lokomotif"
    },
    {
        status: 3,
        description: "Scheduled Maintenance"
    },
    {
        status: 4,
        description: "Under Repair"
    },
    {
        status: 5,
        description: "Idle Lokomotif"
    },
    {
        status: 6,
        description: "Out of Service - Major Repairs"
    },
    {
        status: 7,
        description: "Testing Phase"
    },
    {
        status: 8,
        description: "Retired Lokomotif"
    },
    {
        status: 9,
        description: "Pending Inspection"
    },
    {
        status: 10,
        description: "Emergency Standby"
    }
];
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Creating Report Summary component
&lt;/h2&gt;

&lt;p&gt;This component will be causing error since we are not yet defining API calls service and a helper function. But we will got into it soon.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In &lt;code&gt;components&lt;/code&gt; directory, create a new directory called &lt;code&gt;ReportSummary&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Inside &lt;code&gt;ReportSummary&lt;/code&gt; directory, create a file called &lt;code&gt;index.jsx&lt;/code&gt;. This is the content of the file:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Grid, Typography } from '@mui/material'
import React, { useEffect, useState } from 'react'
import { Chart } from 'chart.js/auto'
import { CategoryScale } from 'chart.js/auto'
import { Bar } from 'react-chartjs-2'
import Grid2 from '@mui/material/Unstable_Grid2/Grid2'
import { getAllLokomotifFromYesterday } from '../../services/lokomotif'
import { groupLokomotifStatusandTotal } from '../../utils/HelperFunctions'

Chart.register(CategoryScale);

function ReportSummary() {

  const [chartData, setChartData] = useState({});
  const [isFetched, setIsFetched] = useState(false);

  useEffect(() =&amp;gt; {
    getAllLokomotifFromYesterday()
      .then((res) =&amp;gt; {
        const data = groupLokomotifStatusandTotal(res);
        const labels = data.map((lokomotif =&amp;gt; lokomotif.status + 1));
        const datasets = {
          label: 'Total lokomotif in database',
          data: data.map((lokomotif =&amp;gt; lokomotif.total)),
          backgroundColor: ["#ad917f"]
        }

        setChartData({
          labels: labels,
          datasets: [datasets],
        })

        setIsFetched(true);

      }).catch((err) =&amp;gt; {
        console.log(err);
      });
  }, []);

  return (
    &amp;lt;Grid 
      container 
      gap={2}
      sx={{
        display:'flex', 
        flexDirection: 'column',
        alignItems: 'left',  
        width: '100%', 
        height: '100%', 
        backgroundColor: '#FDF2EF',
        borderRadius: '1rem',
        padding: '1rem' 
      }}
    &amp;gt; 
      &amp;lt;Grid item&amp;gt;
        &amp;lt;Typography variant='h6' fontWeight={"bold"} sx={{ color: '#855ef5' }}&amp;gt;Lokomotifs Status Statistic in Database&amp;lt;/Typography&amp;gt;
      &amp;lt;/Grid&amp;gt;
      &amp;lt;Grid2 item&amp;gt;
        {
          isFetched &amp;amp;&amp;amp; (
            &amp;lt;Bar 
              data={chartData} 
              options={{
                plugins: {
                  legend: {
                    display: false
                  }
                },
                scales: {
                  x: {
                    title: {
                      text: 'Lokomotif status',
                      display: true
                    }
                  },
                  y: {
                    title: {
                      text: 'Total lokomotif in database',
                      display: true
                    },
                    ticks: {
                      stepSize: 1
                    }
                  },
                }
              }}
            /&amp;gt;
          )
        }
      &amp;lt;/Grid2&amp;gt;
    &amp;lt;/Grid&amp;gt;
  )
}

export default ReportSummary
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Creating a helper function
&lt;/h2&gt;

&lt;p&gt;Since the summary will only contain locomotive status and its total number in database, we need some function to create an object as what we need since API return will be a whole different object in term of structure.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Inside &lt;code&gt;utils&lt;/code&gt; directory, create a file called &lt;code&gt;HelperFunction.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const groupLokomotifStatusandTotal = (lokomotifs) =&amp;gt; {
    const totalStatus = 10;
    let grouppedLokomotif = [];

    for(var i = 0; i &amp;lt; totalStatus; i++) {
        let totalLokomotif = lokomotifs.filter(lokomotif =&amp;gt; lokomotif.status === i.toString()).length;
        let temp = {
            status: i,
            total: totalLokomotif
        }

        grouppedLokomotif.push(temp);
    }

    return grouppedLokomotif;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Creating API call service
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Inside &lt;code&gt;services&lt;/code&gt; directory, create a directory called &lt;code&gt;config&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Inside &lt;code&gt;config&lt;/code&gt; directory, create a file called &lt;code&gt;axios.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import axios from "axios";

const BASE_URL = import.meta.env.VITE_BASE_URL;

const api = axios.create({
    baseURL: BASE_URL,
});

export default api;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Inside &lt;code&gt;services&lt;/code&gt; directory, create a directory called &lt;code&gt;lokomotif&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Inside &lt;code&gt;lokomotif&lt;/code&gt; directory, create a file called &lt;code&gt;index.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import api from "../config/axios";

export const getAllLokomotifFromYesterday = async () =&amp;gt; {
    try {
        const res = await api.get('/lokomotifs');
        const pageSize = res.data.page.totalPages;

        let allLokomotifData = [];
        for(var i = 0; i &amp;lt; pageSize; i++) {
            const resOnPage = await getLokomotifDataByPage(i)
            allLokomotifData = [...allLokomotifData, ...resOnPage];;
        }

        return allLokomotifData;
    } catch (error) {
        console.log(error);
    }
};

const getLokomotifDataByPage = async (page) =&amp;gt; {
    try {
        const res = await api.get(`/lokomotifs?page=${page}`);
        return res.data._embedded.lokomotifs;
    } catch (error) {
        console.log(error);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Running the application
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Make sure Spring Boot: Scheduler Report service is run.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open command prompt from the project directory and run this command:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If the project runs correctly, below is the UI you will see&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--T4g-WFCf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8ijdzpxghl2wj340k7f9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--T4g-WFCf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8ijdzpxghl2wj340k7f9.png" alt="Image description" width="800" height="411"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>javascript</category>
      <category>beginners</category>
      <category>tutorial</category>
      <category>react</category>
    </item>
  </channel>
</rss>
