Skip to content

how to make mistakes

To learn from mistakes, one must make mistakes. To make mistakes one must push forward, despise inertia, and be soberly impatient. For this impatience to be tolerated by others, one must be diligent and continually progress. To progress, one must introspect and evaluate made mistakes. Allow for time to reflect.

Warning: There is a level of effort and diligence one must maintain, as it directly relates to a near supervisor’s loss of sleep.

node.js and mongo (using mongoose) tutorial

NoSQL databases came back into the mainstream when developers needed better performance and were ok with giving up the relational aspect of RDBs (unions, joins, etc). NoSQL is said to be easily scalable horizontally, unlike SQL databases. To use the NoSQL datastore MongoDB with your node instance, you need to install:

The mongoDB schema is dynamic, meaning it gets defined every single time you launch your node server.

So to begin, this is how you define a schema:

var Schema = mongoose.Schema
  , ObjectId = Schema.ObjectID;

var Hobby = new Schema({
    name            : { type: String, required: true, trim: true }
});

var Person = new Schema({
    first_name      : { type: String, required: true, trim: true }
  , last_name       : { type: String, required: true, trim: true }
  , username        : { type: String, required: true, trim: true }
  , hobbies         : [Hobby]
  , shoe_size       : Number
  , eye_color       : String
});

Where Schema is a mongoose interface and ObjectId is a MongoDB datatype. Inside Person (called a collection) you can see the different ways you can define SchemaTypes. The [Hobby] is an array or collection of Embedded Documents (which is itself a Schema, must be defined prior to the main schema). Mongoose also comes with validation middleware, so this acts as your server-side data validation (opposed to client side js).

function validateThis(v){
    return v == 'mike';
}
... first_name      : { type: String, required: true, trim: true, validate: [validateThis, 'your name isnt mike'] } ...

Useful reading on Injection in MongoDB.

And to be able to modify Person we need:

var Person = mongoose.model('Person', Person);

And thats about it for designing schemas!

To save/insert data to a schema, we use the .save() function, which has a callback (as all other db interaction calls).

var person_data = {
    first_name: req.params.first
  , last_name: req.params.last
  , username: req.params.username
};

var person = new Person(person_data);

person.save( function(error, data){
    if(error){
        res.json(error);
    }
    else{
        res.json(data);
    }
});

So person_data is the data we have and want to insert, and as you can see it matches the minimum requirements. After creating a new person object, we can save it to the database. By default, the callbacks have an error and data parameters. They return null by default – no error and no data.

To retrieve some data:

Person.find({}, function(error, data){
    console.log(data);
    res.json(data);
});

Where the first parameter of .find() can be a json object such as

{ username: 'mike' }

which acts as

WHERE username="mike"

(mongoose reference).

And to update a document within a collection, I chose to first check to see if there are actually any users with this username. Then we use the callback parameter

person

which is the actual document with username match to perform alterations and save it. There are other ways to do this.

Person.findOne({ username: req.params.username }, function(error, person){
    if(error){
        res.json(error);
    }
    else if(person == null){
        res.json('no such user!')
    }
    else{
        person.hobbies.push({ name: req.params.hobby });
        person.save( function(error, data){
            if(error){
                res.json(error);
            }
            else{
                res.json(data);
            }
        });
    }
});

I think that’s about it, pull or copy/paste the git repo. Let’s start the server:

node app.js

And add a single document by going to the url:

localhost:3001/adduser/michael/mukhin/mmukhin

To view all documents, go to:

localhost:3001/

And to add hobbies to any user:

localhost:3001/addhobby/mmukhin/beer

GIT repo: https://github.com/mmukhin/psitsmike_mongodb_tutorial

Additional reading: NoSQL server-side javascript injection.

node.js with express, quick facebook connect tutorial

This is a super quick tutorial on how to implement “login with facebook” using the Facebook JavaScript SDK. Bits of this tutorial were taken from the Facebook connect example. To begin, lets install express:

npm install express

Now let’s create an app.js file which we will use to create the server.

var app = require('express').createServer();

// routing
app.get('/', function (req, res) {
  res.sendfile(__dirname + '/index.html');
});

app.listen(8080);

Now in the index.html file, we must first include 2 scripts – jQuery and the Facbeook SDK. The app, first gets initialized with facebook by sending the API Key. Then, we query for the login status to see if we are currently logged in and/or user authorized the app for logins. We add a couple listeners for button clicks, and write the session response function. There is a console.log in there so you can see all that actually gets returned.

<!doctype html>
<html>
<head>
   <title>nodejs - facebook</title>
</head>
<body>
	<div>
           <button id="login">Login</button>
           <button id="logout">Logout</button>
	</div>
    <br>
    <span id="user-info" style="border:1px solid #888"></span>
    <span id="fb-root"></span>

    <script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.min.js"></script>
    <script src="http://connect.facebook.net/en_US/all.js"></script>
    <script>
        // initialize the library with the API key
        FB.init({ appId:'YOUR_APP_ID' });

        // fetch the status on load
        FB.getLoginStatus(handleSessionResponse);

        $('#login').bind('click', function() {
            FB.login(handleSessionResponse);
        });

        $('#logout').bind('click', function() {
            FB.logout(handleSessionResponse);
        });

        // handle a session response from any of the auth related calls
        function handleSessionResponse() {
            FB.api('/me', function(response) {
                console.log(response);
                $('#user-info').html(response.id + ' - ' + response.name);
            });
        }
    </script>
</body>
</html>

Now just load up

node app.js

and go to localhost:8080.

node.js and socket.io multiroom chat tutorial

i’ve been meaning to update the previous chat tutorial to include multiple rooms, here it is.

what i failed to cover in the previous example was how to send messages/broadcast information with socket.io. the following examples assume you define the module:

var io = require('socket.io').listen(app);
io.sockets.on('connection', function (socket) { .... });

from the server, emit to the client’s socket only, using the socket callback:

socket.emit('function', 'data1', 'data2');

from the server, emit to everyone but the client’s socket (to broadcast a message):

socket.broadcast.emit('function', 'data1', 'data2');

from the server, emit globally:

io.sockets.emit('function', 'data1', 'data2');

you see that io was initially defined as the socket.io module, so we emit to all its sockets.

one of the latest updates to socket.io now allows rooms/groups. for a client to join and leave a room:

socket.join('room1');
socket.leave('room1');

to broadcast information to a certain room (excluding the client):

socket.broadcast.to('room1').emit('function', 'data1', 'data2');

to broadcast information globally:

io.sockets.in('room1').emit('function', 'data1', 'data2');

on with the tutorial, in the new app.js, when a user connects and enters their username, he joins room1 by default and broadcasts (only to room1) that they’ve joined. i’ve also added a list of available rooms (room1, room2, room3) and a function that updates that list to reflect the user’s current room.

if the user decides to switch rooms, a client-side function switchRoom is called. the server listens for this call and tells the user’s socket to leave the current room and join the new room, broadcasts locations and updates the room list.

here is the updated app.js:

var app = require('express').createServer()
var io = require('socket.io').listen(app);

app.listen(8080);

// routing
app.get('/', function (req, res) {
  res.sendfile(__dirname + '/index.html');
});

// usernames which are currently connected to the chat
var usernames = {};

// rooms which are currently available in chat
var rooms = ['room1','room2','room3'];

io.sockets.on('connection', function (socket) {

	// when the client emits 'adduser', this listens and executes
	socket.on('adduser', function(username){
		// store the username in the socket session for this client
		socket.username = username;
		// store the room name in the socket session for this client
		socket.room = 'room1';
		// add the client's username to the global list
		usernames[username] = username;
		// send client to room 1
		socket.join('room1');
		// echo to client they've connected
		socket.emit('updatechat', 'SERVER', 'you have connected to room1');
		// echo to room 1 that a person has connected to their room
		socket.broadcast.to('room1').emit('updatechat', 'SERVER', username + ' has connected to this room');
		socket.emit('updaterooms', rooms, 'room1');
	});

	// when the client emits 'sendchat', this listens and executes
	socket.on('sendchat', function (data) {
		// we tell the client to execute 'updatechat' with 2 parameters
		io.sockets.in(socket.room).emit('updatechat', socket.username, data);
	});

	socket.on('switchRoom', function(newroom){
		// leave the current room (stored in session)
		socket.leave(socket.room);
		// join new room, received as function parameter
		socket.join(newroom);
		socket.emit('updatechat', 'SERVER', 'you have connected to '+ newroom);
		// sent message to OLD room
		socket.broadcast.to(socket.room).emit('updatechat', 'SERVER', socket.username+' has left this room');
		// update socket session room title
		socket.room = newroom;
		socket.broadcast.to(newroom).emit('updatechat', 'SERVER', socket.username+' has joined this room');
		socket.emit('updaterooms', rooms, newroom);
	});

	// when the user disconnects.. perform this
	socket.on('disconnect', function(){
		// remove the username from global usernames list
		delete usernames[socket.username];
		// update list of users in chat, client-side
		io.sockets.emit('updateusers', usernames);
		// echo globally that this client has left
		socket.broadcast.emit('updatechat', 'SERVER', socket.username + ' has disconnected');
		socket.leave(socket.room);
	});
});

here is the updated index.html:

<script src="/socket.io/socket.io.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script>
<script>
	var socket = io.connect('http://localhost:8080');

	// on connection to server, ask for user's name with an anonymous callback
	socket.on('connect', function(){
		// call the server-side function 'adduser' and send one parameter (value of prompt)
		socket.emit('adduser', prompt("What's your name?"));
	});

	// listener, whenever the server emits 'updatechat', this updates the chat body
	socket.on('updatechat', function (username, data) {
		$('#conversation').append('<b>'+username + ':</b> ' + data + '<br>');
	});

	// listener, whenever the server emits 'updaterooms', this updates the room the client is in
	socket.on('updaterooms', function(rooms, current_room) {
		$('#rooms').empty();
		$.each(rooms, function(key, value) {
			if(value == current_room){
				$('#rooms').append('<div>' + value + '</div>');
			}
			else {
				$('#rooms').append('<div><a href="#" onclick="switchRoom(\''+value+'\')">' + value + '</a></div>');
			}
		});
	});

	function switchRoom(room){
		socket.emit('switchRoom', room);
	}

	// on load of page
	$(function(){
		// when the client clicks SEND
		$('#datasend').click( function() {
			var message = $('#data').val();
			$('#data').val('');
			// tell server to execute 'sendchat' and send along one parameter
			socket.emit('sendchat', message);
		});

		// when the client hits ENTER on their keyboard
		$('#data').keypress(function(e) {
			if(e.which == 13) {
				$(this).blur();
				$('#datasend').focus().click();
			}
		});
	});

</script>
<div style="float:left;width:100px;border-right:1px solid black;height:300px;padding:10px;overflow:scroll-y;">
	<b>ROOMS</b>
	<div id="rooms"></div>
</div>
<div style="float:left;width:300px;height:250px;overflow:scroll-y;padding:10px;">
	<div id="conversation"></div>
	<input id="data" style="width:200px;" />
	<input type="button" id="datasend" value="send" />
</div>

GIT repo https://github.com/mmukhin/psitsmike_example_2