Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
Ben Nadel at Scotch On The Rock (SOTR) 2010 (London) with: Darren Walker
Ben Nadel at Scotch On The Rock (SOTR) 2010 (London) with: Darren Walker@bayornet )

String Interpolation Using util.format() And util.inspect() In Node.js

By Ben Nadel on

In my blog post yesterday on creating custom error objects in Node.js, you may have seen me use the util.format() method to prepare some of the error data. The util.format() and util.inspect() methods are my two new favorite methods in Node.js. Together, they make string interpolation painless and powerful.


 
 
 

 
 
 
 
 

Even if you've never called the util.format() or util.inspect() methods directly, you're likely already using them in [some] of your Node.js code. I say this because the console.log() method uses util.format() under the hood. And, the util.format() method, in turn, will call util.inspect() in some circumstances.

The util.format() method allows for really easy string interpolation. The first argument represents the interpolation template. Arguments 2-N then reference the values that will be interpolated into the template. As an example:

util.format( "Here is a string: %s, and a number: %d.", "Foo", 4 );

NOTE: If you supply more values than there are interpolation placeholders, the extra arguments will be appended to the result as coerced string values.

In this case, "Foo" will replace "%s" and "4" will replace "%d". You can also pass complex objects in have and util.format() call JSON.stringify() when it encounters the "%j" placeholder. However, I might softly recommend against that. Instead, I would serialize the complex object using util.inspect() and then pass the result into the interpolation template using "%s". I suggest this because util.inspect() has a lot more flexibility and won't break with circular references (or rather, it breaks much more gracefully).

CAUTION: util.inspect() doesn't necessarily produce a value that can be passed back into JSON.parse(). It's not a serialization method - it's a stringification method. Use that understanding when it comes to deciding which methods to use.

To see this in action, I've put together a contrived demo that uses both util.format() and util.inspect() to execute string interpolation:

  • // Require our core node modules.
  • var util = require( "util" );
  •  
  •  
  • // Do something that will execute some string methods.
  • doSomething(
  • 4,
  • {
  • id: 1,
  • name: "Kim",
  • status: "Rock star"
  • }
  • );
  •  
  •  
  • // I am just a contrived function to give some context to the string formatting functions.
  • function doSomething( id, friend ) {
  •  
  • // If the ids don't match, log an error.
  • if ( id !== friend.id ) {
  •  
  • // When logging this error, we're actually using two different string methods -
  • // util.format() and util.inspect(). The .format() method allows for really easy
  • // string interpolation. The .inspect() method provides a smarter "stringify"
  • // method for complex objects that won't break with circular references (it will
  • // inject "[Circular]" tokens in appropriate places in lieu of breaking).
  • logError({
  • type: "InvalidArgument",
  • message: "Friend.id does not match id.",
  • detail: util.format( "Friend with id [%s] does not match given id [%s].", friend.id, id ),
  • extendedInfo: util.format( "Arguments: %s", util.inspect( arguments ) )
  • });
  •  
  • }
  •  
  • }
  •  
  •  
  • // For this demo, we're just going to log the error details to the console. Notice that
  • // the console.log() method also allows for string interpolation, the same way that the
  • // util.format() method does. console.log() actually calls util.format() under the hood.
  • function logError( error ) {
  •  
  • console.log( "Type: %s", error.type );
  • console.log( "Message: %s", error.message );
  • console.log( "Detail: %s", error.detail );
  • console.log( "Extended Info: %s", error.extendedInfo );
  •  
  • }

As you can see, the util.format() is used to generate the "detail" and "extendedInfo" error properties. And, util.inspect() is used to serialize the arguments before they interpolated into the "extendedInfo" property. When we run this code, we get the following console output:

Type: InvalidArgument
Message: Friend.id does not match id.
Detail: Friend with id [1] does not match given id [4].
Extended Info: Arguments: { '0': 4, '1': { id: 1, name: 'Kim', status: 'Rock star' } }

I know it might seem odd to get so fired up about string interpolation. But, the fact that Node.js core provides this functionality out of the box is super awesome. It's just going to make a lot of things a lot easier. And, I think it makes the code easier to read as it removes the noise of having to deal with string concatenation.




Reader Comments

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.