Barris
Barris

Reputation: 1019

node/express - exporting controller functionality

I am building a Node API on express which takes GET requests and uses the parameters supplied by the client to return the results of GET requests made to other API's.

In order to keep the controller thin when adding more API's I would like to export the logic within the controller into a separate .js file, and module.export those functions back in, to be used in the controller. The problem here is that the functions that are being exported do not appear to be visible within the controller.

Pasted below is before and after code to illustrate progress made so far.

app.js (before) - see router.get('/')

var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var request = require('request');

var routes = require('./routes/index');
var users = require('./routes/users');

var app = express();
var router = express.Router();

if ( app.get('env') === 'development') {
  var dotenv = require('dotenv');
  dotenv.load();
};

var prodAdv = require('./lib/prod-adv.js')

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));

app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', routes);
app.use('/users', users);
app.use('/api', router);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  var err = new Error('Not Found');
  err.status = 404;
  next(err);
});

// error handlers

// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
  app.use(function(err, req, res, next) {
    res.status(err.status || 500);
    res.render('error', {
      message: err.message,
      error: err
    });
  });
}

// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
  res.status(err.status || 500);
  res.render('error', {
    message: err.message,
    error: {}
  });
});

router.get('/', function(req, res) {
  request('https://openapi.etsy.com/v2/listings/active?includes=Images&keywords=' + req.param('SearchIndex') + '&limit=100&api_key=' + process.env.ETSY_KEY, function(error, response, body) {
    res.header({'Access-Control-Allow-Origin': '*'});
    var data = JSON.parse(body);
    res.json(data);
  });
});

router.use('*', function(req, res, next) {
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, accept, authorization");
  next();
});


var server = app.listen(9876, function() {
var host = server.address().address;
var port = server.address().port;
  console.log('Example app listening at http://%s:%s',host,port);
});

module.exports = app;

This approach works, returning JSON objects. However the following approach to try and export the code does not work.

apiCaller.js

var express = require('express');
var app = express();
if ( app.get('env') === 'development' ) { var dotenv = require('dotenv'); dotenv.load(); };
var request = require('request');
var call, response;

var call = function(searchIndex) {
  return request('https://openapi.etsy.com/v2/listings/active?includes=Images&keywords=' + searchIndex + '&limit=100&api_key=' + process.env.ETSY_KEY, function(error, response, body) {
    response = JSON.parse(body);
  });
};

module.exports.response = response;
module.exports.call = call;

app.js (after)

var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var squid = require('./lib/apiCaller.js');

var routes = require('./routes/index');
var users = require('./routes/users');

var app = express();
var router = express.Router();

if ( app.get('env') === 'development') {
  var dotenv = require('dotenv');
  dotenv.load();
};

var prodAdv = require('./lib/prod-adv.js')

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));

app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', routes);
app.use('/users', users);
app.use('/api', router);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  var err = new Error('Not Found');
  err.status = 404;
  next(err);
});

// error handlers

// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
  app.use(function(err, req, res, next) {
    res.status(err.status || 500);
    res.render('error', {
      message: err.message,
      error: err
    });
  });
}

// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
  res.status(err.status || 500);
  res.render('error', {
    message: err.message,
    error: {}
  });
});

router.get('/', function(req, res) {
  squid.call(req.param('SearchIndex'));
  res.header({'Access-Control-Allow-Origin': '*'});
  res.json(squid.response);
});

router.use('*', function(req, res, next) {
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, accept, authorization");
  next();
});


var server = app.listen(9876, function() {
var host = server.address().address;
var port = server.address().port;
  console.log('Example app listening at http://%s:%s',host,port);
});

module.exports = app;

What occurs now in the browser is a 200 OK with an empty response body. console.logging the responses return undefined objects.

Upvotes: 0

Views: 3642

Answers (1)

Molda
Molda

Reputation: 5704

You need to rewrite your call function to have a callback since request(...) is asyncronous

var call = function(searchIndex, callback) {
    request('https://openapi.etsy.com/v2/listings/active?includes=Images&keywords=' + searchIndex + '&limit=100&api_key=' + process.env.ETSY_KEY, function(error, response, body) {
        if (!error && response.statusCode == 200) {
            return callback(null, JSON.parse(body));
        }
        callback('error');
    });
};

Only export call function, there's no need to export or even use response and no need for this line

var call, response;

Now you also need to use it a bit different way

router.get('/', function(req, res) {
    res.header({'Access-Control-Allow-Origin': '*'});
    squid.call(req.param('SearchIndex'), function(err, data){
        if(!err) return res.json(data);
        res.json({error: err});
    });    
});

Upvotes: 1

Related Questions