Build and Deploy a Full Stack Realtime Chat Messaging App with Authentication & SMS Notifications
Published Aug 30, 2021 – 3.5 hrs
Link to video here.
Medical Pager
Works with Stream. GetStream.io is the number one Chat Messaging platform that allows you to build scalable and custom chat applications using their APIs.
00:00:00 Intro
00:05:40 Setup
00:11:21 Initial Structure
00:50:07 Client-Side Auth
01:08:57 Server-Side Auth
01:29:32 Client-Server Communication
01:45:15 Channel Container
02:01:57 Create Channel and Sidebar
02:58:22 Edit Channel
03:06:47 Search
03:17:43 Twilio SMS Notifications
03:28:38 Deployment
Intro
Wow.
Node, React, React Context API, Stream.io, Twilio. General Page. Deploy.
Medical Pager.
Login and registration form. Added to Database. Emoji Reactions, Threads for replies, Gifs, Search, Mobile-responsive.
Setup
Empty folder on desktop then open in VS Code
two directories, client and server
in the client folder
install create-react-app
delete src contents and add App.js file and index.js file that connects to index.html
Build out App.js file
install dependencies
npm install stream-chat, stream-chat-react, univeral-cookie
entire code on github
Initial Structure
import everything
Get API KEY from stream website and store in constant variable
Create instance of stream chat
Specify client and theme
Add component folder and build out components ChannelCOntainer.jsx and ChannelListsContainer
Add a new index.js file to hold all of your components so we can just import that file into App.js instead of crowding App.js with lots of import lines.
Uses BEM methodology.
Copy and paste all styles into App.css file from the GitHub gist link, (1800 lines of code).
Note: I know CSS isn’t the purpose of this video but skipping 1800 lines of CSS code in a 3.5 hour video gives you some idea of how long it would take to build this from scratch. Knowing how to build out the CSS is very important.
import the “App.css” file into the “App.jsx” file.
import things into ChannelListContainer.jsx component
Create assets folder to source built-in icons. These are SVGs stored in components and “.png” files and a “.jpg.”
Left Sidebar
Add Functional Comp named Company Header
With Stream you build out the JSX.
Implement a Channel Search
Bring in UseState amd UseEffcte
useChat Content from “stream chat react”
Add input with value and onChange
query, setQuery to useSate with emoty string
prevent default on an onSearch fcuntion
create a loading setLoading state
setQuery(event.target.value)
getChanne;ls
Const getChannels – async ()text -> {}
try catch
better comments VSCode extendion
Add Channel List into Channel List Container
filters pops
chammel Remder Foleten F
List props
if error return type — “team ? (…)
if (loading) return same div
render ()
Create TeamChannelPrview
Will receive a lot of porps. Add a a Preview rht
import Avatar and useChatContext from “stream0chat-react”
simple p tag and “# {channel?.data?.name || channel?.data?.id}
const driect Prview
We do not get back an array
We need to turn the object into a array using object.value().fitler(membrr => {})
Return block of JSX with Avatar component passing it props of image and name and size.
below the avatar add the members name
in the main return block have a dynamic classname
Client-Side Auth
import auth
will get AUTH token if logged in so based on that show or hide he form
hide everything o not logged in.
inside of the Auiht.jsx comp we need useTstate and cookies from “universla colkied” and need to install and import axios.
create layout for auth
dive with auth__form-container and inside form
create a variable isSignup and setIsSignipe with false das default
Nice blue screnn to sign in
out browser side by side with editor
Add a form with onSubmit propeot
isSignup && (..)
this is an alternagiv to the ternary opeator.
add a label with htmlFor properot
Add na inupt with name and type and placeholder and onCHange propoety to add function called handleChage and make this reauire. Cannot have emoty onf subnut
Need to create handleCHange
To see for now change useState for teuw.
username is not oin the block
Add another input for username byt duplicating
Add another inputphonenNUmber
add another input avatar URL
Add input for Passowrd
One more field by repeating password and use isSignuo block
name need to be correct because this is how we are managing data
Add a aith_form-conteinat_field-account
if is Signup ? Already have an account : Don’t have an account
Have a span with an on Click pointing to switch mode and define this up top and setIsSiunup to not previous is signup (state depending in previous state)
diplay an image next to iur form
add a classname to a div and inside add an img with src
Looks great on fullscreen.
Handle the data from all of the inputs with a new state field called form
initial state shuld be am object with fullname, userna,epaddowrd, confiormPassword,
Add onHandleCHange that receives event and setForm and spread input in the field using e.target.name
Keeps track of every single keystroke (need a throttle function)
Forgot to add a fields-content_button tot he form and then conditionally render sign up or sign-in
Add a handle Submit union for that that accept event and prevent the default.
We see the data object in the console. Thisis wjat we want to send to the erver
Server-Side Auth
CD inot the server directory and create new index.jsx file. In the terminal and run npm init-y and now we need to install all the dependencies
keep to terminals open
tweak some scripts in the package.json file
Add basic express server.
specific port or 5000
require dotvevn.confiog so we can use env variable
app.us cors, express.jsons, urlencoded
app.get for the first route using res.send
app.listen(port => {…})
Minimum instance of express application
Add route by requiring routes file at the top and then create a route folder and an auth.js file
require in express and the router
add a router.post() to “signup” and another for “/login”
Create controller folder and “auth.js” file
create login and “isSignup” functions
with module.exports o {signup, oign}
import these functions into the “route/auth.js” file
export router
build out signup function with try catch blocks. inside the catch block throw and error if there’s a problem UI the try block destructure from req.body
import crypto and all other packages
implemt user id with random crypto stream.
Connect form getStream but need to pass some things. But these need to be protect so we will use env variables.
We find these value from the stream dashboard.
Add a .env file in your application
Paste in values formthe stream dashbaord.
const hashedPassword set equal to await bcrypt.hash(passpowrd, 10)
remember 10 is the salet.
const token
res.status(200).json
Creta login function as asuyn function and destrcuture form req.bodya nd connect the client and new instance of StreamChat.getInstance with (api_key and Api_secret)
const {users} = await client.queryUsers()
if no users res.status.400.json()
const suvccess = await bcrypt.compare (password, users)
create new use token const token – serverCLient.createuserToken(I)
if success then send data back res.status(200).json(pass data back here0
else res.statuis(500).json ()incorrect
Client-Server Communication
get data out of it const {} -= form
cont URL – http://local:5000/auth
now make an axio scall sont data = await axios.post()
We have a dynamic url iside of our string
destrcuture token, useriD, hashedpasssord,
crate instance of cookies
then cookies.set(token, tokemn)
repeat three times for username na dfullanem adn iserId
if isSignup set phone number and avatar and hashed password.
Storing eveyrhtingin cookies right
reload browser with window.location.reload.
cookies.get()
if authtoken then client.connectUsr({})
cookies.get()
We are in an object so we need a key
shortcut magoc to format data
need userid to be id and
connect usr accept second parameter
user will be connected and get his messages
Test by create user and logging in as that user
get error and cannot connect
require .env config
click signup
we are logged in in stream dashbaord see if the user has been created.
how do we actually logout
go to client/source.ChannelListCOntainer and juyst below crate a new fucntion and clear the cookies and reload the window.
switch cookies.get from cookies.remove and call window.location.reload
pass logout as a prop
Now wehn we log out we go back ot he sign in
Channel Container
Create right part where messages, inputs for messages, gifs, etc.
In channelContainer ad from stream-chat-react., channel and useChatContext
also import channel inner, createchannel, edit channle
Create Channel component
paste in fro channel inner
Add imports for tall of these componentn in the inex.js file
in channel container destrcutu channel from
Decaltre state field right inside of the App.js fiule
ChannelList container gets a buch of props
Channel Container gets a lot of props as well
Accepting all of these props inside of the ChannelContainer
if isCreating return ()
is isEditing retun ()
const EmptyState function Thsi si the beginning of your chat histoyr.
main return()
Channel and props of empty state indicator, message prop that gets function
ChannelInner component
Add one more import to grab their CSS for better looking components
ChannelContainer
Paste in code for Channel Inner Component created out of the box with stream chat.
We shoud now see some good stuff
Test sending a message
Bring in Message Teme comp (deprecated) but stil used
Add emojis, deelte, edit, by built0in stream channel components
Create Channel and Sidebar
Add a button to add a new channel in TeamChanelList
of type == theme and if type !== theme
import an icon from AddChannel component
Add AddChannel to TeamChannelList but we don’t’ have access tot he props we need to pass it. So we need to send from App.js file to ChannelListCOntainer and Destrcutute props
Setiscreating, SrtCreateType, setIsEditing and type
Now we need to acess from Team ChannelLsisrt to access to the same props from,
We will Switch from this prop drilling to React’s context API with time
Now we can pass the necessary props to the AddChannel component
move into the createChannel Component and import useChatContext from “stream-chat-react”
also import userList from “./”
also import closeCreateChannel from “../assets”
return a div with paragraph input and a second p tag
Add handleChage function hooked tot he input
setChannelName
in CreateChannel add some JSX with some condtiinla
closCreateChannle
Add another logicla bloxk to render ChanneInputname
We don’t have access tot he variable
add useState and set to empty string. import from top
UserList component doesnt exist yet, so lets create it right now
import useEffect and useState. Also bring in avatar and useChat Channel from stream chat react
import invite Icon from assets
return som JSx
import into index.jsx
bring browse to the right and ide ot hr ledt
List Conatiner
UserList
seond helper function componentn const UserItem = function
get users by stting state and leveraging useEffct hook with fulters as dependnecy array
if loading, which need to be ste up as a state filed.
try and catch block to query users from cleitn which we destrcuture from useChatContext
const response – await client.queryUsers()
We haven’t declared filters yet, we don’t need now so hold off.
if reponseusers.lentgh then set users to repoosne.user esle setListMpety to tru and in the catch consol.e erro and afte that then setIsLaoding to false
ListCOntainer
users.map() to redner UserIrem compen and pas some thing sot it, index, key,
in UserIte rddner the Avatar iwht he image name and size props
Add icons to see if we are following or not
Able to toggle between them using new useState field set to false ad conditional logic aysing
add onClick to iv and handleSleetec Function that simple sets selected tp prev sletion and notpreviuos seleftd
Click tot toogel users on and off. But we need to keep track of which cine is toggled.
client.userid || empty steing
We need to pas setSlecteUsees to list
if user was seldted callback with prevuser to prevuser.fgilter(prevusers !== user,id)
removce selected user
else setSlectedUsers (prevuser) [..pevuser, user.id]
if error don’t render list container
needs to be inside of list container
Copy if statement and change if error to if list is empty
Now the userList comp is completed. Long component.
Add a button to create channel hooked up to a createChanel function that is an asyn function that gets the vent and ha a try catch block an prevents default and in the try block new chaheln eauls await client.chahnel() and then await newChahenl.watch
also have cleanup setChannelName, setIsCreatign, setSledcteusers, SetActiveChannel
test adding a new channel and we are in
channel doesn’t aear onthe left isde?
lets fix by going to channel list container and we left filters ot be emoty
cerate filters by gettign a list of all the channels and filter out === team and antoher that finds ===messaing
renaming channelListContainer
setCreateYype, setisCreating, setIsEditing, one more state value
Be able to toggle container baed on the wisth of the field
improt useState ad set togleCONtsiner, setToofel COtainer state thatis initiia;;ly set to false
waht JSx we retunr a fragemtnt hat get a div with channel-list__cointainer ad inside we render ChannelListCOntent and pass als the props
We dont have to pass iseEditing propert
Under that we have a sif channel-list_container-reonseive. witha style that has left proprty set contiionally 0% or -89% to pull from left side.
also a “channel-list__container-toggle” div with clasName and onclick that has setTofflContainer
Also ChannelLostContent and add prop of setTofleCOntainer
SO we have two version, one for response and one for non-responsive
const filters = {}
Finish left side for the
Pass in a few more places
Pass setToffleCOntainer
Need to implemt clicking on a channel
TeamChannelPreview componeent
setAcive Channel and pas in chnnel variable
Checkout Diretcpreview fucntion in “UserList” file
Creating another doctor account and then go to the dashboard and you will see a list of users.
There is an error in logic that gets fixed.
Test gain and now we see name and not id
Edit Channel
In the EditChannel component, import React and useState
build out JSX that includes a closeCreateChannel with seIsEditing prop
Also has a ChannelNameInput and we need to get these value and store in state
Also render out the user List as self closing component and setSeleteced user so we need to get them and store in state.
Add onclick that points to upodateChannel function that is an asyn function and received event ad
Search
ChannelSearch.jsx component and so set const {} from useChat contex
need a few useTsta efor teamCHannels and directChanlles
now inside of the try blocj setchannlerepsons eto client.queryCHannels and pass some data
also query the users with const useRespoinse = client.queryUsers({}) and pass data into the object
put these into a promise.all const channels, users – await Promise.all([channelResponse, userRespsonee])
if channles.lenght setTeamChannels(channels)
if users.lenght setDirectChannelss(users)
if query exist render s a component called ResultsDropdown and we will need to pass a lot fonf info into this component.
teamchannel, directcahhe, loading, stChannel, srtQuer, etc.
const SetChannel to be
ChannelSearhc is currently not passing any data so we need to fix that
Add useEffct to this fuel and add “query” as a dependency array
ode out results deopdown component and import into the index.js file
Paste in the coed since there is not a lot to leanr form here?
in ChannelSearch we import and add
We are now done with the application
Twilio SMS Notifications
Connectso if a user is not online they wil get a text mesage
create a Twilio account
veryfy email
verify phone
No we are hosting our own code. Testing Get a Twilio trial phone number.
in the server.iundex.js const accountSID = process.ene.twilio_account)sid and const authToken -[ procees..enc.Twilio_auth_token
cot twilioClient = require(twilio)
copuy valuesfrom the web insot your .env file
set up one fmore route that will trigger a specif route
app.post(“./)
const {message, user: sender, type, e,ebrs} = req.bidy
cannot be tested in develpment mode.
if type ==== messaeg.ew ebers.forEavh memmber frun t if usr!onlie twilil.clitne.meddage.create({boyd: “”, messageServcieSID: messageServeiID, to })
const messaging servivceSID – process.ebe_twilio and paste this inot the ,env file
We don’t want to send a message to ourselves so we use a filter function to filter out ourselves
res.status.0.send
return res,sattsus(200) not a new message
this is all the twilio code. they make it ery easy.
emojis work, thresds, wrok , we are finally ready tldepluye
Deployment
deploy backend to heroku
downloads and install heroku cli
run heroku login after stopping both servers. cd into the correct directory.
rungit init in the server idfectory
se tht git remote git add, commit, git push heroku master
application is now live. grab the backend URL and go to iur stream and endter inot the dahsboard webhookrul field in stea,.io
ikn the code updateth hsot url
Deploy he front end to netlify and in cd in the font end code to
now are website is live!
group chat with friends