Learning Centre
  • Welcome
  • Install Node.js
  • Hands on with Node.js
    • Preface
    • What is Node.js
    • Node's Ecosystem
    • Simple Servers
    • Advanced Servers
    • Web Applications
    • Appendix
    • Glossary
Powered by GitBook
On this page
  • Building a Chat Web Application
  • Server to Browser Relay
  • Browser to Browser Broadcasting
  • Where can this go?

Was this helpful?

  1. Hands on with Node.js

Web Applications

PreviousAdvanced ServersNextAppendix

Last updated 6 years ago

Was this helpful?

Building a Chat Web Application

It's now time to move onto building a proper web application. Rather than the simple todo application you would build in other systems, for node the standard is a chat application (which is a lot more exciting!).

To do this, we will use and both by .

Primus is a wrapper library over many different libraries. WebSockets is a technology that allows browser clients to communicate with servers. Until WebSockets became a thing in 2011, such functionality was achieved via .

We can install Primus for our project like so:

npm init  # setup our project
npm install --save express  # install express, which we will attach Primus to
npm install --save primus  # install Primus
npm install --save ws  # the WebSockets library we will sue with Primus

Server to Browser Relay

The most basic example of this is the following, which will allow the server to broadcast to all clients, while receiving responses from clients:

'use strict'

// Requires
const express = require('express')
const Primus = require('primus')
const pathUtil = require('path')

// Application
const app = require('express')()
const server = require('http').createServer(app)
const primus = new Primus(server, { transformer: 'websockets' })

// Middlewares
app.get('/', function (req, res) {
	require('fs').createReadStream(pathUtil.join(__dirname, 'socket-client.html')).pipe(res)
})
app.use(function (req, res) {
	res.status(404).send('404 Not Found. 🙁 \n')
})

// Socket
primus.on('connection', function (spark) {
	console.log('connection has the following headers', spark.headers)
	console.log('connection was made from', spark.address)
	console.log('connection id', spark.id)

	// Receive messages
	spark.on('data', function (message) {
		console.log('connection', spark.id, 'sends', message.toString())
	})

	// Send messages
	process.stdin.on('data', function (message) {
		spark.write('The server has spoken: ' + message.toString())
	})

	// Send an initial hello
	spark.write('Hello user. I am the server communicating to you.')
})

// Listen
server.listen(8080)
<!DOCTYPE html>
<html>

<head>
	<title>My Web App</title>
</head>

<body>
	<!-- Scripts -->
	<script src="/primus/primus.js"></script>
	<script>
		// Tell primus to create a new connect to the current domain/port/protocol
		var primus = new Primus()

		// Ask for a response if we receive data
		primus.on('data', function (message) {
			alert(message)
			var response = prompt('What would you like to say?')
			if (response) {
				primus.write(response)
				alert('Sent')
			}
		})
	</script>
	Hello.
</body>

</html>

Browser to Browser Broadcasting

A more useful example of this, is your basic chat app, which allows clients to broadcast to other clients:

'use strict'

// Requires
const express = require('express')
const Primus = require('primus')
const pathUtil = require('path')

// Application
const app = require('express')()
const server = require('http').createServer(app)
const primus = new Primus(server, { transformer: 'websockets' })

// Middlewares
app.get('/', function (req, res) {
	require('fs').createReadStream(pathUtil.join(__dirname, 'chat-client.html')).pipe(res)
})
app.use(function (req, res) {
	res.status(404).send('404 Not Found. 🙁 \n')
})

// Socket
primus.on('connection', function (spark) {
	console.log('connection has the following headers', spark.headers)
	console.log('connection was made from', spark.address)
	console.log('connection id', spark.id)

	// Receive messages
	spark.on('data', function (message) {
		// Broadcast them back to everyone
		primus.write('user ' + spark.id + ' sends ' + message.toString())
	})

	// Send an initial hello
	spark.write('Hello user ' + spark.id + '. I am the server communicating to you.')
})

// Listen
server.listen(8080)
<!DOCTYPE html>
<html>

<head>
	<title>My Web App</title>
	<style>
		.messages {
			max-height: 500px;
			border: 1px solid gray;
			overflow: auto;
		}
		.messageInput {
			width: 100%;
			padding: 1em;
		}
	</style>
</head>

<body>
	<!-- App -->
	<div class="app">
		<ul class="messages"></ul>
		<input type="text" disabled class="messageInput" placeholder="Enter your message here" />
	</div>

	<!-- Scripts -->
	<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0-beta1/jquery.min.js"></script>
	<script src="/primus/primus.js"></script>
	<script>
		// Tell primus to create a new connect to the current domain/port/protocol
		var primus = new Primus()

		// Ready
		$(function () {
			// Fetch
			var $app = $('.app')
			var $messages = $app.find('.messages')
			var $messageInput = $app.find('.messageInput')

			// Enable input once connection is open
			primus.on('open', function () {
				$messageInput.removeAttr('disabled').focus()
			})

			// Receive message
			primus.on('data', function (message) {
				$message = $('<li>', {
					'class': 'message',
					text: message
				})
				$messages.append($message)
			})

			// Send message
			$messageInput.on('keypress', function (event) {
				if (event.keyCode === 13) {  // enter
					var message = $messageInput.val().trim()
					primus.write(message)
					$messageInput.val('')  // clear input
				}
			})
		})
	</script>
</body>

</html>

Where can this go?

It is now your turn to have a go and mash up your own solution. To help you get started, here are a bunch of ideas on how you can extend the chat application:

  • Chat Rooms

    • Add support for two chat rooms

    • Add support for unlimited chat rooms

    • Allow users to change the names of the chat rooms

  • Users

    • Give each user a randomly generated name - e.g. User ${Math.random()}

    • Next to each message, show the user who sent it - e.g.${user.name} says: ${message.text}

    • Show user connection and disconnection events as messages in the chat - e.g. ${user.name} joined the chat"

    • Remember the user's details if they leave the page and come back - e.g. localstorage or sessions

    • Allow the user to change their name

    • Show user name change events as messages in the chat - e.g.${oldName} changed their name to ${newName}

    • Give each user their own randomly selected color - e.g. style="color: hsl(50,50,50);"

    • Create a sidebar that lists all active members in the chat room

    • Allow users to specify their email

    • If a user has an email specified, display their avatar next to their username in the message listing

    • When a user changes their details, automatically update all prior mentions of their details

  • Abstractions

  • Messages

    • Relative times

    • Markdown support

    • Webkit notifications

Test it:

Test it:

Experiment with pre-processors - could help with this

Primus
ws
Arnout Kazemier
WebSockets
several dodgy workarounds
http://localhost:8080/
http://localhost:8080/
DocPad