justjs: node.js tutorials

New here? You might want to start at the beginning.

10/1 '12

Node lessons from Startup Weekend Philly

(Or "What I Did On My Summer Vacation")

 
Hey, did you miss me? Sorry, I've been busy and haven't written much about node lately. But this weekend I attended Startup Weekend Philly. I was tasked to build an interactive website with a lot of data entry needs in a hella hurry. I wanted to make sure I learned something and had fun, so I built it in node and deployed it to Heroku. And here it is.
 
Some things I learned from the experience:

Mongoose is a big win with table-like data

Sometimes I recommend going straight to MongoDB. But if your data has a clear schema and you don't need inheritance, Mongoose is absolutely worth using. It adds exactly the right amount of schemasauce to MongoDB and no more. You can write this:
 
 
var company = new db.Company(req.body);
  company.save(function(err, company) {
    res.redirect('/companies/' + company.slug);
    return;
  });
 
... And rely on Mongoose to validate 'req.body' and only use the properties that are in the schema to populate the company. That's so good.
 
Even better, you can do this when editing an existing object:
 
req.company.set(req.body);
 
Only the fields that are actually in req.body get overridden, etc. And your validators get called.
 

Middleware is super super great for everything

 
Here's middleware to make sure we have a valid company slug in the URL and make it part of the request:
 
function needCompany(req, res, next) {
  db.Company.findOne({ slug: req.params.slug }, function(err, company) {
    if (err) {
      return fail404(res);
    }
    req.company = company;
    next();
  });
}
 
Now I can write:
 
 
app.post('/companies/:slug/add-job', needCompany, function(req, res) {
  // Update the req.company object etc.
});
 
Notice that req.params.slug is available to the middleware because the slug parameter is part of the route. Sweet.
 

nunjucks is a great template engine

 
nunjucks is definitely ready for prime time. For those of us who think Jade is too weird, too imcomplete or too fussy about not letting you put an overrideable block inside an element's class attribute if that's how you roll (and it totally is - I like overrideable classes on the body element)... and those of us who discover we're the only node developer at Startup Weekend and really don't want to antagonize the frontend developer we just met with a template language that doesn't let them write normal-looking HTML... nunjucks is  the way to go. nunjucks is a port of the jinja template engine from the Python world, which is part of the same extended family as django and twig. 
 
I ran into almost no nasty surprises. I added custom filters easily (the famous javascript dateFormat function for instance). And the frontend developer I'd just met, who has previously used PHP templates, had zero trouble getting up to speed with it. 
 
You can refer to the jinja templates developer documentation, it's a very faithful port.
 
I did hit ONE nasty surprise: if you already have a variable 'job' that was passed into a template (including, say, the template that 'include'd the template you're looking at), and you introduce another variable by that name in a 'for' loop, the original variable shadows the loop variable and you keep getting the same value.
 
I worked around it by changing the variable name in the loop. It looks like a better way is to just deliver macros in your include files. Macros can take parameters, etc. I didn't get a chance to try that yet.

Installing node on Windows isn't hard, don't be afraid to tell people to do it

I'm a Mac guy. But Alex, my teammate and frontend developer for the weekend, is a Windows guy and he had never seen node before. He got it up and running in 20 minutes, pulled the app from our private github repo, and was pushing code within the hour. You can totally just do:
 
node server.js
 
You don't have to carry out some weird marriage of IIS and node as I've seen at some demos, for development at least.

Heroku + node = super easy... but you need Amazon S3 for file uploads

Heroku loves node and is super easy to deploy to via git push. But your users can't upload files to Heroku. Use Amazon S3 cloud storage.
 
Calls to fs.writeFile, etc. will work and may be useful for temporary files during the execution of the current request. But that file may be gone on the next page refresh because your dyno may be running on another machine in their cloud by then.
 
Fortunately, there's a great npm module for S3 called knox. It's by TJ. Unfortunately, there are fussy things nobody tells you about S3 that are not fun to learn during a rapid application development process!
 
Those fussy things would be:
 
  • Make sure you specify the Content-Type explicitly or it'll be served as application/octet-stream
  • Make sure you  specify 'x-amz-acl': 'public-read' if you want your file to be visible to web browsers
  • Before you hit "create" on that bucket, pick a region with "read-after-write consistency" if you need to be sure that file can be read as soon as you're told S3 has saved it! As far as I know the only region you can NOT use is "US Standard," which is the default. I used "US Oregon."
  • Specify the right endpoint explicitly or you'll get "307" redirects, which knox does not follow automatically for you even when using convenience APIs like putFile. knox will invoke your callback without an error, but your file will never make it to S3. Ouch.
Here's some code. Note the 'endpoint' argument. This is really just the hostname knox will do PUT requests to. It has to be made up of your bucket name, plus the subdomain name for the Amazon S3 region your bucket is in, or the 307 redirect issue will clobber you.
 
var knox = require('knox');

// Use 'heroku config set S3_BUCKET=bucketname' to tell heroku your production S3 bucket name
// In dev, default to a dev bucket

var bucket = (process.env.S3_BUCKET ? process.env.S3_BUCKET : 'mycompanydev');

var s3Options = {
  key: (process.env.S3_KEY ? process.env.S3_KEY : 'xxxxx'),
  secret: (process.env.S3_SECRET ? process.env.S3_SECRET : 'xxxxx'),
  bucket: bucket,
  // Without specifying the oregon endpoint knox returns
  // a triumphant result telling us we got redirected with a 307
  endpoint: bucket + '.s3-us-west-2.amazonaws.com'
};

var s3 = knox.createClient(s3Options);

// Now we can copy a temporary file from Heroku to S3

var file = req.files.file;

s3.putFile(src, dst, { 'x-amz-acl': 'public-read', 'Content-Type': file.type }, function(err, res) {
    ...
  });

Go to startup weekends

They are full of creative people with ideas worth pursuing. You'll meet cool folks, have fun and stretch your brain. You might even start a company. 

Bring a skeleton app

I arrived at Startup Weekend with a skeleton application that rendered pages with tabs and breadcrumbs, stored things in MongoDB and was ready to deploy to Heroku. That helped a lot - Node is a very "a la carte" environment in which you can spend a lot of time making decisions about the fundamentals, time you don't have at a startup weekend. My skeleton app should have been even more complete - I should have made sure I already had user logins baked in via Passport local authentication, for instance, with a reasonable security policy. Live and learn!

Evangelize Node

It's not hard! I was the only person developing with Node at this particular startup weekend. I let folks know I'd be using Node on the facebook wall for the event, and many people came by to check out what I was doing. It's a great opportunity to spread the word.
 

 

blog comments powered by Disqus