# Web Applications

## 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 [Primus](https://github.com/primus/primus) and [ws](https://github.com/websockets/ws) both by [Arnout Kazemier](https://github.com/3rd-Eden).

Primus is a wrapper library over many different [WebSockets](https://en.wikipedia.org/wiki/Websocket) 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 [several dodgy workarounds](http://slides.com/balupton/what).

We can install Primus for our project like so:

```bash
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:

{% tabs %}
{% tab title="socket-server.js" %}

```javascript
'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)
```

{% endtab %}

{% tab title="socket-client.html" %}

```markup
<!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>
```

{% endtab %}
{% endtabs %}

Test it: <http://localhost:8080/>

### Browser to Browser Broadcasting

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

{% tabs %}
{% tab title="chat-server.js" %}

```javascript
'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)
```

{% endtab %}

{% tab title="chat-client.html" %}

```markup
<!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>
```

{% endtab %}
{% endtabs %}

Test it: <http://localhost:8080/>

## 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
  * Experiment with pre-processors - [DocPad](http://docpad.org) could help with this
* Messages
  * Relative times
  * Markdown support
  * Webkit notifications


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://learn.bevry.me/hands-on-with-node.js/web-applications.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
