2

I'm building a simple server in JavaScript running on the Node.js platform. The server accepts JavaScript Objects, parses them, and executes commands based on what it finds; fairly simple stuff. I call those Objects Envelopes (because you open up envelopes in real life and do things based on what they say. Get it? I'm so clever.).

I have 3 files which are called: server.js, envelope.js, and user-handler.js. In my tests, the user sends a "user" Envelope for the server to parse, eventually so the server creates a new user and adds them into the MongoDB instance I've set up locally.

However, in user-handler.js, I've encountered a weird error: TypeError: object is not a function. I have no idea why I'm getting this error, because the Envelope object is definitely being imported correctly. Below you'll find the 3 files in question. Any and all answers are greatly appreciated!

EDIT: For clarification, here is the exact stacktrace that I get when running a test (which I've also included):

Server listening on port 31337
Accepted input from user...
Opened up the envelope...
[Function: Envelope]
Envelope verified successfully
It's a 'user' command...
Starting to parse the envelope...
Going to create a new user...
Now to save new user...

/home/ME/PROJECT/lib/user-handler.js:52
      console.log(new Envelope({
                  ^
TypeError: object is not a function
    at Object.module.exports.parseEnvelope (/home/ME/PROJECT/lib/user-handler.js:52:19)
    at Envelope.open (/home/ME/PROJECT/lib/envelope.js:34:28)
    at Socket.<anonymous> (/home/ME/PROJECT/lib/server.js:22:22)
    at Socket.EventEmitter.emit (events.js:95:17)
    at Socket.<anonymous> (_stream_readable.js:720:14)
    at Socket.EventEmitter.emit (events.js:92:17)
    at emitReadable_ (_stream_readable.js:392:10)
    at emitReadable (_stream_readable.js:388:5)
    at readableAddChunk (_stream_readable.js:150:9)
    at Socket.Readable.push (_stream_readable.js:113:10)

server.js

"use strict";

var net = require("net")
  , mongoose = require("mongoose")
  , dbConf = require("../conf")
  , Envelope = require("./envelope")
  , UserHandler = require("./user-handler")
  , uri, db, server;

uri = (dbConf.uri || "mongodb://localhost") // URI
  + (":" + (dbConf.port || 27017) + "/") // Port
  + (dbConf.db || "DATABASE"); // Database
db = mongoose.connect(uri);

server = net.createServer(function(socket) {
  socket.on("data", function(data) {
    var input, output;

    //try {
      input = new Envelope(JSON.parse(data.toString()));
      console.log("Accepted input from user...");
      output = input.open();
      console.log("And the output is: ");
    /*} catch (error) {
      var errorString = String(error).split(": ");
      output = new Envelope({
        header: {
          type: "error"
        },
        body: {
          errorType: errorString[0],
          errorMessage: errorString[1]
        }
      });
    }*/

    console.log(JSON.stringify(output));
  });
});

server.listen(31337, function() {
  console.log("Server listening on port 31337"); 
});

envelope.js

"use strict";

var userHandler = require("./user-handler");

var Envelope = function Envelope(data) {
  for (var key in data) {
    this[key] = data[key];
  }
};

Envelope.prototype = {
  constructor: Envelope,

  verify: function() {
    console.log(this.constructor);

    if (this.header && this.header.type && this.body) {
      console.log("Envelope verified successfully");
      return true;
    } else {
      console.log("Envelope did not verify successfully");
      return false;
    }
  },

  open: function() {
    var self = this;

    console.log("Opened up the envelope...");
    if (self.verify()) {
      switch (self.header.type) {
      case "user":
        console.log("It's a 'user' command...");
        return userHandler.parseEnvelope(self);
        break;

      default:
        return new Envelope({
          header: {
            type: "empty"
          }
        });
      }
    } else {
      return new Envelope({
        header: {
          type: "error"
        },
        body: {
          errorType: "MissingDataError",
          errorMessage: "Missing either: header, body, or header.type"
        }
      });
    }
  }
};

module.exports = Envelope;

user-handler.js

"use strict";

var User = require("../models/user")
  , Envelope = require("./envelope");

module.exports = {
  parseEnvelope: function(env) {
    var header = env.header;
    var body = env.body;

    console.log("Starting to parse the envelope...");
    switch (header.method) {
    case "create":
      console.log("Going to create a new user...");
      var newName = body.name || {
        first: "",
        last: ""
      };

      var newUser = new User({
        username: body.username,
        password: body.password,
        name: newName,
        email: body.email || ""
      });

      console.log("Now to save new user...");
      /*newUser.save(function(err) {
        if (err) {
          console.log("Ruh roh, there was an error...");
          console.log(String(err));
        } else {
          console.log("New user saved successfully to the database...");
          return newUser;
        }
      });*/
      /*var response = new Envelope({
        header: {
          type: "response",
          requestType: "user",
          requestMethod: "create"
        },
        body: {
          _id: newUser._id,
          username: newUser.username,
          password: newUser.password,
          name: newUser.name,
          email: newUser.email
        }
      });*/

      console.log(new Envelope({
        test: "Hello, world!"
      }));

      //console.log(response);
      break;

    default:
      return new Envelope({
        header: {
          type: "error"
        },
        body: {
          error: "MissingHeaderMethodError",
          errorMessage: "Missing header parameter: method"
        }
      });
      break;
    }
  }
};

tests/user.js

"use strict";

var net = require("net")
  , client;

client = net.connect({host: "localhost", port: 31337}, function() {
  client.end(JSON.stringify(
    {
    "header": {
      "type": "user",
      "method": "create"
    },
    "body": {
      "username": "john_doe",
      "password": "password1",
    }
    }
  ));
});
10
  • If it's not a function then what is it? What shows up if you just log the value of "Envelope"? Commented May 11, 2013 at 15:09
  • 1
    "Aaahh puns" - That's not a pun. Also, there's no such thing as a "JSON object". Commented May 11, 2013 at 15:11
  • @Pointy If I just log the value of "Envelope" from within "user-handler.js", I get that same error which I added in. From anywhere else, it returns an empty object. Commented May 11, 2013 at 15:13
  • @nnnnnn Fine, you're right. It's not a pun. I still felt clever, though. Commented May 11, 2013 at 15:14
  • 1
    ...in fact, I wonder if it's because you are replacing the provided exports object. I know that's allowed, but consider that NodeJS caches the exports object so that you don't keep parsing the same module every time it's requested. Now consider the order server -> Envelope -> user-handler -> Envelope. When user-handler requests the Envelope, the parsing of Envelope has already started, so NodeJS won't start it again, but it does need to return something, so it returns the default exports object because you've not yet replaced it at the bottom of the Envelope module. Commented May 11, 2013 at 15:39

1 Answer 1

2

The problem is probably that you have circular imports. Your Envelope imports the user-handler and the user-handler import the Envelope. Since the Envelope is "just" an envelope, it problably shoudn't know about the user-handler. So you could refactor it in the following way:

Envelope.js

open: function(callback) {
var self = this;

console.log("Opened up the envelope...");
if (self.verify()) {
  switch (self.header.type) {
  case "user":
    console.log("It's a 'user' command...");
    return callback(self);
    break;

  default:
    return new Envelope({
      header: {
        type: "empty"
      }
    });
  }
} else {
  return new Envelope({
    header: {
      type: "error"
    },
    body: {
      errorType: "MissingDataError",
      errorMessage: "Missing either: header, body, or header.type"
    }
  });
}
}

server.js

output = input.open(UserHandler.parseEnvelope);

Now you don't need to import your user-handler.js in your Envelope.js and everything should work smoothly.

Sign up to request clarification or add additional context in comments.

1 Comment

This makes sense. After asking the question, I thought a bit more about what I was doing wrong, and the circular import does make sense. I wasn't totally familiar with how Node handles imports, but @squint's explanation makes perfect sense. I'll give this a shot, thank you!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.