This is the second part of the Building Real time private chatting app using GoLang. In thefirst partof the Series, we did the setup of the React and GoLang application.
In this part, you will implement below-listed features in your application,
- Registration.
- Checking the uniqueness of the username before registration.
- Login.
- Checking the Session of a user (when the user navigates to the home page).
Setting Up the Authentication Page
In this first section, we will set up the authentication page. In the below Image as you can see we have Tabs for Login and Registration. If you click the Login Tab then you will see the Login form and If you click on Registration Tab then you will see the Registration Form.
Here we will implement these Tabs with the React Hooks. In the container of those Tabs, we will render theLogin
andRegistration
components. The below code will do the same, open the Authentication.js file and add the below code.
authentication/authentication.js:
// Real time private chatting app using GoLang, React and mongodb // @author Shashank Tiwari import React, { useState } from 'react'; import './authentication.css'; import Login from './login/login'; import Registration from './registration/registration' function Authentication() { const [activeTab, setTabType] = useState('login'); const [loaderStatus, setLoaderStatus] = useState(false); const changeTabType = (type) => { setTabType(type); } const getActiveClass = (type) => { return type === activeTab ? 'active' : ''; }; const displayPageLoader = (shouldDisplay) => { setLoaderStatus(shouldDisplay) } return ( <React.Fragment> <div className={`app__loader ${loaderStatus ? 'active': ''}`}> <img alt="Loader" src="/loader.gif"/> </div> <div className='app__authentication-container'> <div className='authentication__tab-switcher'> <button className={`${getActiveClass('login')} authentication__tab-button`} onClick={() => changeTabType('login')} > Login </button> <button className={`${getActiveClass('registration')} authentication__tab-button`} onClick={() => changeTabType('registration')} > Registration </button> </div> <div className='authentication__tab-viewer'> {activeTab === 'login' ? <Login displayPageLoader={displayPageLoader}/> : <Registration displayPageLoader={displayPageLoader}/>} </div> </div> </React.Fragment> ); } export default Authentication;
Explanation:
1. First, we importedLogin
andRegistration
components to display them inside the Tab container.
- First, we show the loader based on the
loaderStatus
state variable. Then with the help of
activeTab
state variable, we show the active Tab. IfactiveTab
is equal"login"
then show Login Tab otherwise Registration Tab.
Implementing Registration Feature
Let’s start by implementing the Registration feature. I don’t think I have much to talk about the importance of the registration feature. First, we’ll write the client-side code. Later we will write node APIs and all code related to it.
=>So the first step is writing MarkUp, so open theRegistration.js
and write down the below code markup in thereturn()
statement.
Registration.js:
return ( <div className="app__register-container"> <div className="app__form-row"> <label>Username:</label> <input type="email" className="email" onKeyDown={handleKeyDownChange} onKeyUp={handleKeyUpChange}/> </div> <div className="app__form-row"> <label>Password:</label> <input type="password" className="password" onChange={handlePasswordChange}/> </div> <div className="app__form-row"> <span className="error-message">{registrationErrorMessage? registrationErrorMessage : ''}</span> </div> <div className="app__form-row"> <button onClick={registerUser}>Registration</button> </div> </div> );
Explanation:
- Here we have two input boxes wrapped by Div tag. The first Input Tag is for entering the username and the second Input tag is for a password.
- On the first Input tag, we will implement the debouncing feature using
onKeyDown
andonKeyUp
events. The events will callhandleKeyDownChange
andhandleKeyUpChange
respectively. - Then we show the error message just in case if anything goes wrong.
- Lastly, we have a registration button, when you will click on the button it will call
registerUser
function.
To make your markup work, you will have to write some code inside your component function. Open theRegistration.jsand write down the below code. The below code is full code of the Registration Page.
=>In the below code, we will use api-service.js
file to register the new User.
=>The application will redirect the user to the Home Page as soon as the user finishes the registration process.
authentication/registration/registration.js:
// Real time private chatting app using GoLang, React and mongodb // @author Shashank Tiwari import React, {useState} from 'react'; import { withRouter } from 'react-router-dom'; import { isUsernameAvailableHTTPRequest, registerHTTPRequest } from "./../../../services/api-service"; import { setItemInLS } from "./../../../services/storage-service"; import './registration.css' function Registration(props) { const [registrationErrorMessage, setErrorMessage] = useState(null); const [username, updateUsername] = useState(null); const [password, updatePassword] = useState(null); let typingTimer = null; const handlePasswordChange = async (event) => { updatePassword(event.target.value); } const handleKeyDownChange = (event) => { clearTimeout(typingTimer); } const handleKeyUpChange = (event) => { const username = event.target.value; typingTimer = setTimeout( () => { checkIfUsernameAvailable(username); }, 1200); } const checkIfUsernameAvailable = async (username) => { props.displayPageLoader(true); const isUsernameAvailableResponse = await isUsernameAvailableHTTPRequest(username); props.displayPageLoader(false); if (!isUsernameAvailableResponse.response.isUsernameAvailable) { setErrorMessage(isUsernameAvailableResponse.message); } else { setErrorMessage(null); } updateUsername(username); } const registerUser = async () => { props.displayPageLoader(true); const userDetails = await registerHTTPRequest(username, password); props.displayPageLoader(false); if (userDetails.code === 200) { setItemInLS('userDetails', userDetails.response) props.history.push(`/home`) } else { setErrorMessage(userDetails.message); } }; return ( <div className="app__register-container"> <div className="app__form-row"> <label>Username:</label> <input type="email" className="email" onKeyDown={handleKeyDownChange} onKeyUp={handleKeyUpChange}/> </div> <div className="app__form-row"> <label>Password:</label> <input type="password" className="password" onChange={handlePasswordChange}/> </div> <div className="app__form-row"> <span className="error-message">{registrationErrorMessage? registrationErrorMessage : ''}</span> </div> <div className="app__form-row"> <button onClick={registerUser}>Registration</button> </div> </div> ); } export default withRouter(Registration);
Explanation:
- Let’s start from the top first, we have imported all the necessary functions from
api-service
andstorage-service
. - Then we create three state variable
registrationerrormessage
,username
andpassword
. handlePasswordChange
function will just update the state variable, whenever anyone types the password in the password field.handleKeyDownChange
andhandleKeyUpChange
will help us to implement the denounce feature on the username input field.handleKeyUpChange
will call thecheckIfUsernameAvailable
to check the uniqueness of the username, This topic is discussed in depth in the next section.- The
registerUser
function makes an API call to register the user in out Chat server.
To complete the registration process you will have to write code in GoLang as well. Basically, writing GoLang code will piece of a cake for you, because you don’t have much to write. Let’s get to it, Open theroutes.goand add the below the route.
routes.go:
route.HandleFunc("/registration", handlers.Registertation).Methods("POST")
After that, you have to write a few lines of code inside the handlers.Registertation
package, just to handle the request/response and call the method which just inserts the data into Database.
Add the below code, inside theroute-handlers.gofile. TheRegistertation()
function will take care of the/registration
route.
handlers/route-handlers.go:
// Real time private chatting app using GoLang, React and mongodb // @author Shashank Tiwari //Registertation function will login the users func Registertation(responseWriter http.ResponseWriter, request *http.Request) { var userDetailsRequestPayload UserDetailsRequestPayloadStruct decoder := json.NewDecoder(request.Body) requestDecoderError := decoder.Decode(&userDetailsRequestPayload) defer request.Body.Close() if requestDecoderError != nil { response := APIResponseStruct{ Code: http.StatusBadRequest, Status: http.StatusText(http.StatusBadRequest), Message: constants.ServerFailedResponse, Response: nil, } ReturnResponse(responseWriter, request, response) } else { if userDetailsRequestPayload.Username == "" { response := APIResponseStruct{ Code: http.StatusBadRequest, Status: http.StatusText(http.StatusBadRequest), Message: constants.UsernameCantBeEmpty, Response: nil, } ReturnResponse(responseWriter, request, response) } else if userDetailsRequestPayload.Password == "" { response := APIResponseStruct{ Code: http.StatusInternalServerError, Status: http.StatusText(http.StatusInternalServerError), Message: constants.PasswordCantBeEmpty, Response: nil, } ReturnResponse(responseWriter, request, response) } else { userObjectID, registrationError := RegisterQueryHandler(userDetailsRequestPayload) if registrationError != nil { response := APIResponseStruct{ Code: http.StatusInternalServerError, Status: http.StatusText(http.StatusInternalServerError), Message: constants.ServerFailedResponse, Response: nil, } ReturnResponse(responseWriter, request, response) } else { response := APIResponseStruct{ Code: http.StatusOK, Status: http.StatusText(http.StatusOK), Message: constants.UserRegistrationCompleted, Response: UserDetailsResponsePayloadStruct{ Username: userDetailsRequestPayload.Username, UserID: userObjectID, }, } ReturnResponse(responseWriter, request, response) } } } }
Explanation:
- In the above code, you will first check the validation. If anything wrong with that, we will send the proper error code with an Error message.
- And in the end, if everything is valid or in more precise words if the request is valid than you will Register a user by calling
RegisterQueryHandler()
. - The
RegisterQueryHandler()
function is defined inside thequery-handler.go, which inserts a new user in MongoDB.
Open thequery-handler.gofile and write down the below code. As you have figured it by now, You will just have to insert the records in MongoDB in the below code.
handlers/query-handler.go:
// Real time private chatting app using GoLang, React and mongodb // @author Shashank Tiwari // RegisterQueryHandler function will check username from the database func RegisterQueryHandler(userDetailsRequestPayload UserDetailsRequestPayloadStruct) (string, error) { if userDetailsRequestPayload.Username == "" { return "", errors.New(constants.UsernameCantBeEmpty) } else if userDetailsRequestPayload.Password == "" { return "", errors.New(constants.PasswordCantBeEmpty) } else { newPasswordHash, newPasswordHashError := utils.CreatePassword(userDetailsRequestPayload.Password) if newPasswordHashError != nil { return "", errors.New(constants.ServerFailedResponse) } collection := config.MongoDBClient.Database(os.Getenv("MONGODB_DATABASE")).Collection("users") ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) registrationQueryResponse, registrationError := collection.InsertOne(ctx, bson.M{ "username": userDetailsRequestPayload.Username, "password": newPasswordHash, "online": "N", }) defer cancel() registrationQueryObjectID, registrationQueryObjectIDError := registrationQueryResponse.InsertedID.(primitive.ObjectID) if onlineStatusError := UpdateUserOnlineStatusByUserID(registrationQueryObjectID.Hex(), "Y"); onlineStatusError != nil { return " ", errors.New(constants.ServerFailedResponse) } if registrationError != nil || !registrationQueryObjectIDError { return "", errors.New(constants.ServerFailedResponse) } return registrationQueryObjectID.Hex(), nil } }
Explanation:
- In the above code, the First thing we do the validation of the incoming data if case any issue we return an error.
- Then we will generate the Password Hash for a user-entered password.
- After that, you will connect the MongoDB database and insert the record into the database.
- And that’s it. You have created a new user.
Checking for a unique username
It is very important that before registration, you check the uniqueness of usernames. It’s obvious that you can’t have two users with the same username.
First thing first, let’s go through the Markup written in registration.jsfor showing an alert message if we will find the username already present in the database. Here you don’t have much to write, just an indicator that shows the particular username is taken.
In the below Markup, we have used the React state to hide and show the Message by using theusernameAvailable
state property ofRegistration Componentclass.
authentication/resgistration/resgistration.js:
<!-- Real time private chatting app using GoLang, React and mongodb @author Shashank Tiwari --> <div className="app__form-row"> <span className="error-message">{registrationErrorMessage? registrationErrorMessage : ''}</span> </div>
The above markup will hide/show only if you write something in your input tag. Then we have used Hooks to define the States ofRegistration Component,
authentication/resgistration/resgistration.js:
const [registrationErrorMessage, setErrorMessage] = useState(null); const [username, updateUsername] = useState(null); const [password, updatePassword] = useState(null);
After that add the below code in Registration Component class, The below code will check the username uniqueness by Making an HTTP request to your GoLang server. Open theregistration.jsand update the code of checkUsernameAvailability()
method,
authentication/resgistration/resgistration.js:
// Real time private chatting app using GoLang, React and mongodb // @author Shashank Tiwari const checkIfUsernameAvailable = async (username) => { updateUsername(username); props.displayPageLoader(true); const isUsernameAvailableResponse = await isUsernameAvailableHTTPRequest(username); props.displayPageLoader(false); if (!isUsernameAvailableResponse.response.isUsernameAvailable) { setErrorMessage(isUsernameAvailableResponse.message); } else { setErrorMessage(null); } }
Explanation:
- First, we will update the state property
username
, then we are calling thedisplayPageLoader()
method using React props. - Then we will call the
isUsernameAvailableHTTPRequest()
, which eventually make an HTTP request to check the uniqueness of username. - And based on the response of the HTTP server we will set the value of the
registrationErrorMessage
State.
This is not complete yet, you still have to code for your GoLang part. Writing GoLang routes and its helper will be the same as it was in the last section, In fact, it’s going to be identical for GoLang routes. So first as usual open theroutes.goand add the below the route.
routes.go:
route.HandleFunc("/isUsernameAvailable/{username}", handlers.IsUsernameAvailable)
Now our express route is added, let’s write a method to handle that request in theroutes-handlers.gofile. Open that file and add the below code,
handles/routes-handlers.go:
// Real time private chatting app using GoLang, React and mongodb // @author Shashank Tiwari //IsUsernameAvailable function will handle the availability of username func IsUsernameAvailable(responseWriter http.ResponseWriter, request * http.Request) { type usernameAvailableResposeStruct struct { IsUsernameAvailable bool `json:"isUsernameAvailable"` } var response APIResponseStruct var IsAlphaNumeric = regexp.MustCompile(`^[A-Za-z0-9]([A-Za-z0-9_-]*[A-Za-z0-9])?$`).MatchString username: = mux.Vars(request)["username"] // Checking if username is not empty & has only AlphaNumeric charecters if !IsAlphaNumeric(username) { response: = APIResponseStruct { Code: http.StatusBadRequest, Status: http.StatusText(http.StatusBadRequest), Message: constants.UsernameCantBeEmpty, Response: nil, } ReturnResponse(responseWriter, request, response) } else { isUsernameAvailable: = IsUsernameAvailableQueryHandler(username) if isUsernameAvailable { response = APIResponseStruct { Code: http.StatusOK, Status: http.StatusText(http.StatusOK), Message: constants.UsernameIsAvailable, Response: usernameAvailableResposeStruct { IsUsernameAvailable: isUsernameAvailable, }, } } else { response = APIResponseStruct { Code: http.StatusOK, Status: http.StatusText(http.StatusOK), Message: constants.UsernameIsNotAvailable, Response: usernameAvailableResposeStruct { IsUsernameAvailable: isUsernameAvailable, }, } } ReturnResponse(responseWriter, request, response) } }
First, we have checked if the username that we received is valid or not. Then If you notice we have called IsUsernameAvailableQueryHandler()
method to check the usercount of the same usernames. This method will basically check the counts of the given username and returns that count to the callee.
Open thequery-handler.goand add the below code, in the below code you just a run a MongoDB query and return the response to the caller method.
handles/query-handler.go:
// Real time private chatting app using GoLang, React and mongodb // @author Shashank Tiwari // GetUserByUsername function will return user datails based username func GetUserByUsername(username string) UserDetailsStruct { var userDetails UserDetailsStruct collection: = config.MongoDBClient.Database(os.Getenv("MONGODB_DATABASE")).Collection("users") ctx, cancel: = context.WithTimeout(context.Background(), 10 * time.Second) _ = collection.FindOne(ctx, bson.M { "username": username, }).Decode( & userDetails) defer cancel() return userDetails } // IsUsernameAvailableQueryHandler function will check username from the database func IsUsernameAvailableQueryHandler(username string) bool { userDetails: = GetUserByUsername(username) if userDetails == (UserDetailsStruct {}) { return true } return false }
Explanation:
- In the function
IsUsernameAvailableQueryHandler()
, we are callingGetUserByUsername()
function which gives the user details of given username. - If we get an empty response from
GetUserByUsername()
function, then we can conclude that there no such user in the database.
Now, this completes your Checking for a unique username features. Congrats, you have added one more feature to your application.
Implementing Login Feature:
Implementation of the Login feature is going to pretty easy and almost identical to above-implemented features. First, we will start with Markup, then component and in the end, we will write some GoLang just like we did in the last two sections.
The below markup will render the login form, which will have two fields username and password. Open the login.jsand add the below code inreturn()
method,
authentication/login/login.js:
// Real time private chatting app using GoLang, React and mongodb // @author Shashank Tiwari return ( <div className='app__login-container'> <div className='app__form-row'> <label>Username:</label> <input type='email' className='email' onChange={handleUsernameChange} /> </div> <div className='app__form-row'> <label>Password:</label> <input type='password' className='password' onChange={handlePasswordChange} /> </div> <div className='app__form-row'> <span className='error-message'> {loginErrorMessage ? loginErrorMessage : ''} </span> </div> <div className='app__form-row'> <button onClick={loginUser}>Login</button> </div> </div> );
Explanation:
handleUsernameChange()
andhandlePasswordChange()
functions will update the React state with respective details.loginUser()
methods will make an HTTP request to/login
endpoint usingloginHTTPRequest
function which is written inside theapi-service.js
file.
Now we will complete the rest if code i.e. left in the Login component. The below code will handle the Login form submission. Open thelogin.jsand write down the below code,
authentication/login/login.js:
// Real time private chatting app using GoLang, React and mongodb // @author Shashank Tiwari import React, { useState } from 'react'; import { withRouter } from 'react-router-dom'; import { loginHTTPRequest } from './../../../services/api-service'; import { setItemInLS } from './../../../services/storage-service'; import './login.css'; function Login(props) { const [loginErrorMessage, setErrorMessage] = useState(null); const [username, updateUsername] = useState(null); const [password, updatePassword] = useState(null); const handleUsernameChange = (event) => { updateUsername(event.target.value); }; const handlePasswordChange = (event) => { updatePassword(event.target.value); }; const loginUser = async () => { props.displayPageLoader(true); const userDetails = await loginHTTPRequest(username, password); props.displayPageLoader(false); if (userDetails.code === 200) { setItemInLS('userDetails', userDetails.response); props.history.push(`/home`); } else { setErrorMessage(userDetails.message); } }; // return method should come here.... } export default withRouter(Login);
Explanation:
- Then we have imported the
loginHTTPRequest
andsetItemInLS
function. - Since here we are using State Hooks and we will use the updated State Variables to while making a Login HTTP request.
- In
loginUser()
method, first, we will call thedisplayPageLoader()
method using Reactprops
n order to show the loading screen. loginHTTPRequest()
function will Login the user by making an HTTP call to the server, here we are directly passing the username and password with the updated value.- Once the Login is successful, then we will redirect the user to the
/home
page. Also, we will store the userId in local storage so that we can use it later in the application. - The
handleUsernameChange()
andhandlePasswordChange()
methods will update the keys of state object respectively.
Let’s write the GoLang part, Here we will do three things First we will add the route. Second, we will add a method to handle the request/response of the route and at the end method to perform the Login.
So let’s just add the route, open theroutes.goand add the below route.
routes.go:
route.HandleFunc("/login", handlers.Login).Methods("POST")
The /login
route is added to its respective file, now let’s write the method to handle the response to the /login
route. Open the routes-handlers.go and add the below code,
handles/routes-handlers.go:
// Real time private chatting app using GoLang, React and mongodb // @author Shashank Tiwari //Login function will login the users func Login(responseWriter http.ResponseWriter, request *http.Request) { var userDetails UserDetailsRequestPayloadStruct decoder := json.NewDecoder(request.Body) requestDecoderError := decoder.Decode(&userDetails) defer request.Body.Close() if requestDecoderError != nil { response := APIResponseStruct{ Code: http.StatusBadRequest, Status: http.StatusText(http.StatusBadRequest), Message: constants.UsernameAndPasswordCantBeEmpty, Response: nil, } ReturnResponse(responseWriter, request, response) } else { if userDetails.Username == "" { response := APIResponseStruct{ Code: http.StatusBadRequest, Status: http.StatusText(http.StatusBadRequest), Message: constants.UsernameCantBeEmpty, Response: nil, } ReturnResponse(responseWriter, request, response) } else if userDetails.Password == "" { response := APIResponseStruct{ Code: http.StatusInternalServerError, Status: http.StatusText(http.StatusInternalServerError), Message: constants.PasswordCantBeEmpty, Response: nil, } ReturnResponse(responseWriter, request, response) } else { userDetails, loginErrorMessage := LoginQueryHandler(userDetails) if loginErrorMessage != nil { response := APIResponseStruct{ Code: http.StatusNotFound, Status: http.StatusText(http.StatusNotFound), Message: loginErrorMessage.Error(), Response: nil, } ReturnResponse(responseWriter, request, response) } else { response := APIResponseStruct{ Code: http.StatusOK, Status: http.StatusText(http.StatusOK), Message: constants.UserLoginCompleted, Response: userDetails, } ReturnResponse(responseWriter, request, response) } } } }
Explanation:
- First, we will do the validation if everything goes fine, then we will call function
LoginQueryHandler()
to do the login related operations. - Just in case, if anything goes wrong with Validation then we send the proper response with Valid Error Message.
- The function
LoginQueryHandler()
defined inside the query-handlers.go file.
Open thequery-handler.gofile and write down the below code. In the below code, we will write the logic behind the code to make the user online.
handlers/query-handler.go:
// Real time private chatting app using GoLang, React and mongodb // @author Shashank Tiwari // UpdateUserOnlineStatusByUserID will update the online status of the user func UpdateUserOnlineStatusByUserID(userID string, status string) error { docID, err := primitive.ObjectIDFromHex(userID) if err != nil { return nil } collection := config.MongoDBClient.Database(os.Getenv("MONGODB_DATABASE")).Collection("users") ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) _, queryError := collection.UpdateOne(ctx, bson.M{"_id": docID}, bson.M{"$set": bson.M{"online": status}}) defer cancel() if queryError != nil { return errors.New(constants.ServerFailedResponse) } return nil } // LoginQueryHandler function will check username from the database func LoginQueryHandler(userDetailsRequestPayload UserDetailsRequestPayloadStruct) (UserDetailsResponsePayloadStruct, error) { if userDetailsRequestPayload.Username == "" { return UserDetailsResponsePayloadStruct{}, errors.New(constants.UsernameCantBeEmpty) } else if userDetailsRequestPayload.Password == "" { return UserDetailsResponsePayloadStruct{}, errors.New(constants.PasswordCantBeEmpty) } else { userDetails := GetUserByUsername(userDetailsRequestPayload.Username) if userDetails == (UserDetailsStruct{}) { return UserDetailsResponsePayloadStruct{}, errors.New(constants.UserIsNotRegisteredWithUs) } if isPasswordOkay := utils.ComparePasswords(userDetailsRequestPayload.Password, userDetails.Password); isPasswordOkay != nil { return UserDetailsResponsePayloadStruct{}, errors.New(constants.LoginPasswordIsInCorrect) } if onlineStatusError := UpdateUserOnlineStatusByUserID(userDetails.ID, "Y"); onlineStatusError != nil { return UserDetailsResponsePayloadStruct{}, errors.New(constants.LoginPasswordIsInCorrect) } return UserDetailsResponsePayloadStruct{ UserID: userDetails.ID, Username: userDetails.Username, }, nil } }
Explanation:
- There is a lot of things going on here, First, we will do the validation if everything goes fine, then we will actually fetch the result from the database and verify it with the data sent from the React application.
- After validation and stuff, we will fetch the record of the user by using his/her username with the help of function
GetUserByUsername()
. - If you get a result from the database using the username, then go ahead compare the password of a user by using
utils.ComparePasswords()
method. - After comparing the password, based on the result you will make the status of the user online using function
UpdateUserOnlineStatusByUserID()
.
Checking User’s Session
It is very critical to check the session of the user before allowing the users to chat and read messages. To check the session of the user, we will make an HTTP call. Whenever a user visits the home page of the application we will check the session.
=>Here we will use React’s Hooks to mimic the behavior of i.e.componentDidMount()
. In this method, we will make an HTTP call to check the session of the user. Open theHome.jsand write down the below code,
home/home.js:
// Real time private chatting app using GoLang, React and mongodb // @author Shashank Tiwari const useFetch = (props) => { const [internalError, setInternalError] = useState(null); const userDetails = getItemInLS('userDetails'); useEffect(() => { (async () => { if (userDetails === null || userDetails === '') { props.history.push(`/`); } else { const isUserLoggedInResponse = await userSessionCheckHTTPRequest( userDetails.userID ); if (!isUserLoggedInResponse.response) { props.history.push(`/`); } else { const webSocketConnection = connectToWebSocket(userDetails.userID); if (webSocketConnection.webSocketConnection === null) { setInternalError(webSocketConnection.message); } else { listenToWebSocketEvents() } } } })(); }, [props, userDetails]); return [userDetails, internalError]; };
Explanation:
- In this method, first, we will call the
setRenderLoadingState()
method to show a loading screen while we make check the session of the user by making an HTTP call. - To check the session of the user, we need the user id of that user. We can get the user details using
getItemInLS()
function, which gives the user details stored in Local Storage. - Once we will get the user ID then we can call the
userSessionCheckHTTPRequest()
function, this method we will make an HTTP call to check if the user is logged in or not. - If the response says, User is logged in then we will make set the username in our component state.
- If the user is not logged in then we will redirect the user to the Home page.
Now let’s add the route for session check service in theroutes.gofile. Here we will check the online status of the user, and based on the online status we will determine the session of that user. So add the below route in theroutes.gofile.
routes.go:
route.HandleFunc("/userSessionCheck/{userID}", handlers.UserSessionCheck)
As always, we have to write method inside theroutes-handlers.gofile to handle the response for the/userSessionCheck
route. As you can see, in this case, we are calling UserSessionCheck()
to handle the response for the/userSessionCheck
.
Open theroutes-handlers.goand add the below code,
handlers/routes-handlers.go :
// Real time private chatting app using GoLang, React and mongodb // @author Shashank Tiwari //UserSessionCheck function will check login status of the user func UserSessionCheck(responseWriter http.ResponseWriter, request *http.Request) { var IsAlphaNumeric = regexp.MustCompile(`^[A-Za-z0-9]([A-Za-z0-9_-]*[A-Za-z0-9])?$`).MatchString userID := mux.Vars(request)["userID"] if !IsAlphaNumeric(userID) { response := APIResponseStruct{ Code: http.StatusBadRequest, Status: http.StatusText(http.StatusBadRequest), Message: constants.UsernameCantBeEmpty, Response: nil, } ReturnResponse(responseWriter, request, response) } else { uerDetails := GetUserByUserID(userID) if uerDetails == (UserDetailsStruct{}) { response := APIResponseStruct{ Code: http.StatusOK, Status: http.StatusText(http.StatusOK), Message: constants.YouAreNotLoggedIN, Response: false, } ReturnResponse(responseWriter, request, response) } else { response := APIResponseStruct{ Code: http.StatusOK, Status: http.StatusText(http.StatusOK), Message: constants.YouAreLoggedIN, Response: uerDetails.Online == "Y", } ReturnResponse(responseWriter, request, response) } } }
Explanation:
- In the above code, First, we will check for the userId validation. If the validation fails, we will send the user not found a response.
- If validation passes, then we will check the online status of the user in the Database by using userId.
- And depending on the online status we will send the response to Angular Application.
Now to check the online status, we need to fetch the online from the database with the help of GetUserByUserID()
function. This method will basically return the user details and we will check the online status by writing this condition uerDetails.Online == "Y"
.
Open thequery-handlers.gofile and add the below method,
handlers/query-handlers.go:
// Real time private chatting app using GoLang, React and mongodb // @author Shashank Tiwari // GetUserByUserID function will return user datails based username func GetUserByUserID(userID string) UserDetailsStruct { var userDetails UserDetailsStruct docID, err := primitive.ObjectIDFromHex(userID) if err != nil { return UserDetailsStruct{} } collection := config.MongoDBClient.Database(os.Getenv("MONGODB_DATABASE")).Collection("users") ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) _ = collection.FindOne(ctx, bson.M{ "_id": docID, }).Decode(&userDetails) defer cancel() return userDetails }
And I assume the above code needs no explanation since you are just executing a MongoDB query and returning the user details.
That’s it.
Conclusion
Now let’s conclude this part by recapping what we did in this part.
- We implemented a Registration feature.
- We implemented a feature to check the uniqueness of the username before registration.
- We added Login functionality in our application.
- In the end, we added a feature to check the session of a User.
In the next part of the series, we will connect our React application to the socket and implement the Realtime chat list for the application.
So stay tuned it’s going to be more interesting.
sir,
i am not able to find part-3 and part-4 of your golang private chat app project