Q&A: ImageMagick and Code Organization in Express.js

We continue with the Q&A from our readers. This is a question from Ian, the reader of Rapid Prototyping with JS.

We continue with the Q&A from our readers. This is a question from Ian, the reader of Rapid Prototyping with JS.

OK you convinced me. I just bought that too. I’m pretty much jumping in with both feet to a Node.js/Express.js/JavaScript direction from PHP/Ruby so it’s full-throttle or nothing at all. :) Will be setting up my blog in Wintersmith. I see that’s covered in RPJS too so it covers many of technologies I’ve chosen to run with.

By way of a recommendation I’d love to see some coverage of something like cheerio in there for web scraping or HTML/XML feed processing. I’ve got some feeds to work with and it seems like cheerio is one of the best ways to go about it at the moment. In addition, what image manipulation libraries would you recommend working with? For the test project that I’ve set myself I’m pulling in from multiple feeds to build an API but each entry in each feed has a potentially large (up to 10MB image) associated with it. I need to grab this, save it somewhere, shrink it down to proportionate 800×600 (4:3 ratio) dimensions with cropping if the dimensions aren’t equal to that. There seems to be quite a few forks of node-imagemagick but are any of those good to go with? I’ve heard that node-vips is also pretty good. Are there any that you recommend?

Finally with regards to code organisation, with regards to the above (feed processing and image manipulation), how would you recommend structuring and organising your code with Express.js? They’re not particular associated with the routing logic and I’ll need to re-use these methods for each feed (10 feeds in all) so I need to keep them modular by nature. Any guidance would be much appreciated. I think I’m getting the rest down pretty quickly. :)

Thanks for the books Azat, they’re extremely helpful.

Kind regards,


Hi Ian,

Thank you for your questions!

I like Wintersmith, and one of my team members recently used it for DocuSign engineering blog instead of Jekyll.

For the image manipulation, there’s an imagemagick module for Node.js. I build a small server that resizes images for Storify which you can download at github.com/azat-co/ic. The main script looks like this:

var express = require ('express');
var http = require('http');
var util = require('util');
var url = require('url');
var querystring = require('querystring');
var fs = require('fs');
var im = require('imagemagick');

var app = express();
	app.set('port', process.env.PORT||3000);
app.get('/',getImage, convertImage, outputImage);
function getImage(req,res,next) {

	// console.log(req.query.u)
	// res.send(req.query.u)
	var imageUrl = url.parse(req.query.u);
	var pathname = imageUrl.pathname;
	var filename = pathname.substring(pathname.lastIndexOf('/')+1);
	// console.log(filename)
	req.gravity = req.query.g || "Center";
	req.cfilename = "converted_"+req.width+"x"+req.height+"_"+req.gravity+"_"+req.filename;
	//TODO: check if image already exists 
	fs.stat(filename, function(err,stats){
		// if (err) console.log(err);
		if (!err&&stats.isFile()) {
			console.log("Located "+filename)
			//we already have the image
		else {
			//download NEW image
			console.log("Downloading "+filename)
			var imageReq = http.request(imageUrl, function(imageRes) {
				console.log("Got response: " + imageRes.statusCode);
				var stream = fs.createWriteStream(filename);
				imageRes.on('end', function () {
					console.log('File is downloaded: '+filename);
			imageReq.on('error', function(e) {
				console.log("Got error: " + e.message);


function convertImage(req,res,next){
	//check if converted image exists
	fs.stat(req.cfilename, function(err,stats){
		// if (err) console.warn(err);
		if (err||!stats.isFile()){
			//convert new image version
			console.log("new converted image: "+req.cfilename);
			im.identify(req.filename, function(err, features){
				if (err) console.warn(err);
				  srcPath: req.filename,
				  dstPath: req.cfilename,
				  width: req.width,
				  height: req.height,
				  quality: 1,
				  gravity: req.gravity,
				}, function(err, stdout, stderr){
		else {
			//we already have this version of the image
			console.log('Located '+req.cfilename);

function outputImage(req,res){
	//output image
	fs.readFile(req.cfilename, function(err, data) {
		if (err) console.warn(err); // Fail if the file can't be read.
    	res.writeHead(200, {'Content-Type': 'image/jpeg'});
	    res.end(data); // Send the file data to the browser.

app.listen(app.get('port'), function(){console.log('Server is running');});

A year ago when I was writing the code above, imagemagick had some bugs. So you might want to check out alternatives like gm.

Note: to make those Node.js modules work, you’ll need GraphicsMagick or ImageMagick to be installed in addition to your Node.js setup.

As for your second question, the best way is to create separate files/modules and include them into the application:

var imageUtils = require('image-utils')
var output = imageUtils.process(data)

I’m not sure why would you need something outside of a route though, because most of the things we need in a server happens as a response to a request. The best way to organize your code for a route is to use middlewares. You can read more about middlewares in the Intro to Express.js: Parameters, Error Handling and Other Middleware post.

If you need to re-use the same functions in different routes, you can set them up as a catch-all middlewares:

app.use('*', catchAllHandler);
catchAllHandler = function(req, res, next) {
  return next();

For more detailed recommendations on the code organization, please take a look at the Express.js Guide book.

Author: Azat

Techies, entrepreneur, 20+ years in tech/IT/software/web development expert: NodeJS, JavaScript, MongoDB, Ruby on Rails, PHP, SQL, HTML, CSS. 500 Startups (batch Fall 2011) alumnus. http://azat.co http://github.com/azat-co

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.