Categories
Node.js Tips

Node.js Tips — POST Requests, Express Route Params, S3 Data, and More

Like any kind of apps, there are difficult issues to solve when we write Node apps. In this article, we’ll look at some solutions to common problems when writing Node apps.

Get the Browser Language in Express

To get the browser language in an Express app, we can use the req.headers['accept-language'] property to get the accept-language header.

Then request.acceptsLanguages method also gets the accepted language header.

For instance, we can write:

const express = require('express');
app.get('/translation', (req, res) => {
  const lang = req.acceptsLanguages('fr', 'es', 'en');
  if (lang) {
    console.log(lang);
  }
  else {
    console.log('None of [fr, es, en] is accepted');
  }
  //...
});

acceptsLanguages makes a list of languages that the app can accept.

If one of the values are in the Accepts-Language header, then it returns true ,

Otherwise, it returns false .

There’s also the accepts module that does the same thing.

To install it, we run:

npm install accepts

Then we can use it by writing:

const accepts = require('accepts');
const http = require('http');

const app = (req, res) => {
  const accept = accepts(req);
  switch (accept.type(['json', 'html'])) {
    case 'json':
      res.setHeader('Content-Type', 'application/json');
      res.write(JSON.stringify({ hello: 'world' ));
      break;
    case 'html':
      res.setHeader('Content-Type', 'text/html');
      res.write('<b>hello, world</b>');
      break;
    default:
      res.setHeader('Content-Type', 'text/plain');
      res.write('hello, world');
      break;
  }
  res.end()
}

http.createServer(app).listen(3000)

We use the accept module to check the Accept header. We can check the kind of language that it requests in the Accept header. It has the languages method to get the browser language from the Accepts-Language header.

How to Construct HTTP POST Request with Form Params

We can construct an HTTP POST request with form parameters with the querystring module.

To install it, we run:

npm i querystring

Then we can write:

const querystring = require('querystring');

axios.post(
  '/login',
  querystring.stringify({
    username: 'abcd',
    password: '12345',
  }),
  {
    headers: {
      "Content-Type": "application/x-www-form-urlencoded"
    }
  }
)
.then((response) => {
  console.log(response);
});

We called the querystring.stringify method to construct a query string with the username and password keys. Also, we set the headers with the Content-Type header set to “application/x-www-form-urlencoded” . This way, we send the query string as form data.

How to Configure Dynamic Routes with Express.js

We can configure dynamic routes with Express by allowing parameters.

For instance, we can write:

app.get('/article/:name', (req, res) => {
  res.render(req.params.name);
});

We can also restrict the allowed values of id by writing:

app.get('/article/:name(article|article2|article3)', (req, res) => {
  const name = req.params.name;
  res.render(name);
});

We restrict the name parameter to be one of article1 , article2 ,or article3 .

In both examples, we get the parameter value by using the req.params.name property.

req.params has the value of name .

Catch Express body-parser Error

We can create our own middleware to catch errors raised by body-parser .

For instance, we can write:

app.use((error, req, res, next) => {
  if (error instanceof SyntaxError) {
    sendError(res, 'error');
  } else {
    next();
  }
});

We check if a SyntaxError is raised by using error instanceof SyntaxError. If it is, then we call sendError to process the error. Otherwise, we call next to call the next middleware in the chain.

Return Hostname with Node.js

We can use the os module’s hostname method to return the hostname.

For instance, we can write:

const os = require("os");
const hostname = os.hostname();

to do that.

Listing Objects from AWS S3 in a Node App

We can get items stored in S3 with the aws-sdk package.

For instance, we can write:

const AWS = require('aws-sdk');
AWS.config.update({
  accessKeyId: 'key',
  secretAccessKey: 'secret',
  region: 'region'
});

const s3 = new AWS.S3();

const params = {
  Bucket: 'bucket',
  Delimiter: '/',
  Prefix: '/path/prefix'
}

s3.listObjects(params, (err, data) => {
  if (err){
    return console.log(err);
  }
  console.log(data);
});

We authenticate with AWS. Then we get the bucket with the Bucket property.

Delimiter is the path separator.

Prefix is the prefix for the path and bucket data.

Then we call listObjects with the path.

The callback’s data parameter has the bucket data.

Conclusion

If it’s not created when we join it, then it’ll be created. We can get data from S3. Express routes can be made dynamic with parameters. We can send form data as a query with a POST request with Axios

Categories
Node.js Tips

Node.js Tips — Express Templates and Requests, and MongoDB Queries

Like any kind of apps, there are difficult issues to solve when we write Node apps.

In this article, we’ll look at some solutions to common problems when writing Node apps.

Close the Express Server

If we need to close the Express server, we just call close on it to close it.

It’s returned from listen and it’s an HTTP server.

For instance, we can write:

const server = app.listen(3000);
server.close();

Full Text Search in MongoDB and Mongoose

We can do a search on Mongoose by creating an index on the identifier of the schema.

Indexes will speed up searches.

For instance, we can write:

const schema = new Schema({
  name: String,
});

schema.index({
  name: 'text',
});

Then we can write:

Model.find({
    $text: {
      $search: searchString
    }
  })
  .skip(20)
  .limit(10)
  .exec((err, docs) => {
    //...
  });

to do the search.

We use the $search operator to do a search on the entry.

skip skip the number of entries given in the argument.

limit limits the number of arguments.

Get the Domain that Originated the Request in Express

We can use req.get to get the hostname from the host header.

We can also use thr origin header for cross-origin requests.

To get the host header, we write:

const host = req.get('host');

And we write:

const origin = req.get('origin');

to get the origin.

Accessing Express.js Local Variables on Client-Side JavaScript

We can pass our data from our template and then parse it in there.

To do that, we write:

script(type='text/javascript').
 const localData =!{JSON.stringify(data)}

We stringified the data so that it’ll be interpolated in the template.

Add Class Conditionally with Jade/Pug

To add a class conditionally, we can write:

div.collapse(class=typeof fromEdit === "undefined" ? "edit" : "")

We put out JavaScript expression right inside the parentheses and it’ll interpolate the class name for us.

Store DB Config in a Node or Express App

We can store our config in a JSON file.

For instance, we can write:

const fs = require('fs'),
configPath = './config.json';
const parsed = JSON.parse(fs.readFileSync(configPath, 'UTF-8'));
exports.config = parsed;

We read the config with readFileSync and then call JSON.parse on the returned text string.

Exclude Some Fields from the Document with Mongoose

To exclude some fields from being returned, we convert it in the tranform method by using the delete on it to remove the property we want.

For instance, we can write:

UserSchema.set('toJSON', {
  transform(doc, ret, options) {
    delete ret.password;
    return ret;
  }
});

We call set to add a method with the transform method to transform the data.

ret has the returned result.

Then we call delete on it.

Listen on HTTP and HTTPS for a Single Express App

We can listen to HTTP and HTTPS with one Express app.

To listen to HTTP requests, we just have to read in the private and certificate files.

Then we can create a server with them.

For instance, we can write:

const express = require('express');
const https = require('https');
const http = require('http');
const fs = require('fs');
const  app = express();

const options = {
  key: fs.readFileSync('/path/key.pem'),
  cert: fs.readFileSync('/path/cert.pem'),
  ca: fs.readFileSync('/path/ca.pem')
};

http.createServer(app).listen(80);
https.createServer(options, app).listen(443);

We read in the files with readFileSync .

Then we pass the whole thing into the https.createServer method to create our server.

We still need http.createServer to listen to HTTP requests.

Get Data Passed from a form in Express

We can get the data passed from a form in Express with the body-parser package.

We use the urlencoded middleware that’s included with it.

For instance, we can write:

const bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({ extended: true }));

app.post('/game', (req, res) => {
  res.send(req.body);
});

Once we have ran urlencoded middleware, it’ll parse form data from POST requests.

extended means we can also parse JSON in the request body.

Then in our route, we can get the data with req.body .

Loop in Jade /Pug Template Engine

We can write a loop with Pug like a JavaScript for loop.

For instance, we write:

- for (let i = 0; i < 10; i) {
  li= array[i]
- }

Conclusion

We can have loops with Pug.

Also, we can read config from a JSON file.

Express can listen to both HTTP and HTTPS requests.

We can transform MongoDB data as we query them.

To add text search capability, we can add an index to speed up the search and use the $search operator.

Categories
Node.js Tips

Node.js Tips — Change Directory with exec, Move Files, and Nested Schemas

Like any kind of apps, there are difficult issues to solve when we write Node apps.

In this article, we’ll look at some solutions to common problems when writing Node apps.

Move File to a Different Partition or Device in Node.js

We can move file to a different partition or device by specifying the path of the destination of our write stream.

We read the file into a read stream and pipe it to a write stream.

For instance, we can write:

const fs = require('fs');
`
const rs = fs.createReadStream('source/file');
const ws = fs.createWriteStream('destination/file');
`
rs.pipe(ws);
rs.on('end', () => {
  fs.unlinkSync('source/file');
});

We call createReadStream with the source file path to read the file from a stream.

Then we call createWriteStream to create a write stream with the destination path.

We call pipe on the read stream with the write stream as the argument to pipe the read stream to the write stream.

This will copy the file from the source to the destination.

Once the copying is done, we call unlinkSync to delete the file from the source path.

We know when the copying is done when the end event is emitted.

To make our lives easier, we can use the fs.extra module’s move method.

We install it by running:

npm install fs.extra

Then we can use the move method by specifying the source and destination paths.

For instance, we can write:

const fs = require('fs.extra');
fs.move('foo.txt', 'bar.txt', (err) => {
  if (err) {
    throw err;
  }
  console.log("success");
});

We called fs.move with the source file path as the first argument.

The 2nd argument is the destination path.

The last is the callback which is run when the move operation is done.

Find Deprecation Warnings

We can find deprecation warnings by using the --trace-deprecation or --throw-deprecation options when we run our app.

For instance, we can run:

node --trace-deprecation app.js

or:

node --throw-deprecation app.js

— trace-deprecation logs a stack trace and --throw-deprecation throw an error.

We should also make sure that we include a callback function in async methods if we get something like:

(node:4346) DeprecationWarning: Calling an asynchronous function without callback is deprecated.

Calling async functions without callbacks is a big deprecated feature.

For instance, we should write:

const fs = require('fs');
//...
`
fs.writeFile('foo.txt', data, 'utf8', (error) => {
  // ...
});

The 4th argument is the callback function.

Node.js exec does not work with the “cd ” Shell Command

Each command is run in its own shell with exec, so using cd only affect the shell process that cd is run on.

If we want to change the current working directory, we’ve to set the cwd option.

For instance, we can write:

exec('git status', {
  cwd: '/repo/folder'
}, (error, stdout, stderr) => {
  if (error) {
    console.error(error);
    return;
  }
  console.log(stdout);
  console.error(stderr);
});

We switch to the /repo/folder directory by setting the cwd option in the object we passed in as the 2nd argument.

Then we can run git status on /repo/folder instead of whatever directory the app is running in.

Schema within a Schema with Mongoose

We can define a schema within a schema with Mongoose.

This way, we can store relational data with it.

For instance, we can write:

const UserSchema = new Schema({
  name: String,
  tasks: [{
    type: Schema.ObjectId,
    ref: 'Task'
  }]
});
`
const TaskSchema = new Schema({
  name: String,
  user: {
    type: Schema.ObjectId,
    ref: 'User'
  }
});

We have 2 schemas that reference each other.

We created a one to many relationships between UserSchema and TaskSchema with the tasks array.

The ref specifies the schema that we want to access.

An array indicates that it’s one to many.

Likewise, in TaskSchema , we define the user field which creates a belongs to relationship with User .

We specify an object with the 'User’ as the value of ref .

Then when we query a user, we can get all its tasks by using the populate method:

User.find({}).populate('tasks').run((err, users) => {
  //...
});

We pass in the child field we want to access, which is tasks .

Conclusion

We can define nested schemas with Mongoose to create relationships.

There are several ways to move files.

We can run node with some options to find deprecation warnings.

To change the current working directory with exec , we can set the cwd property.

Categories
Node.js Tips

Node.js Tips — Simple OAuth, Wait for Elements, and Minimizing Window

As with any kind of app, there are difficult issues to solve when we write Node apps. In this article, we’ll look at some solutions to common problems when writing Node apps.

Wait for an Element to be Visible with Puppeteer

To wait for an element to be visible, we can use the waitForSelector method.

For example, we can write:

page.waitForSelector('#myId', { visible: true })

This waits for the element with the ID myId to be in the DOM.

visible is true ensures that it’s visible before waiting stops.

Minimize or Close Window to the System Tray and Restore Window Back from Tray

To minimize the window to the system tray, we can write:

const BrowserWindow = require('browser-window'),
`
const mainWindow = new BrowserWindow({
  width: 650,
  height: 650,
  title: "window",
  icon:'./icon.png'
});
`
mainWindow.on('minimize',(event) => {
  event.preventDefault();
  mainWindow.hide();
});
`
mainWindow.on('close', (event) => {
  if(!application.isQuiting){
    event.preventDefault();
    mainWindow.hide();
  }
  return false;
});

We call event.preventDefault to prevent the default minimization action when we listen to the minimize event. Then we call hide to hide it in the tray.

We do the same thing when we listen to the close event. If the app isn’t quitting, then we call preventDefault to prevent it from quitting. Then we call mainWindow.hide() to hide it instead. To restore from the tray, we add new menu entries that we can click to restore the window when we click.

For example, we write:

const contextMenu = Menu.buildFromTemplate([
  {
    label: 'restore app',
    click(){
      mainWindow.show();
    }
  },
  {
    label: 'exit',
    click(){
      application.isQuiting = true;
      application.quit();
    }
  }
]);

We call buildFromTemplate with an array to create a context menu.

It has one entry to call mainWindow.show() to show the mainWindow .

And we have an entry that we click to exit the app with application.quit() .

OAuth Authentication with the Twitter API and Express

We can add Twitter authentication to our Express app by using the oauth library with Express. First, we fill in the callback URL with the URL that we want to invoke when authentication is done. It should be set to /session/callback if the example below is used.

Then we write:

const express = require('express');
const oauth = require('oauth');
const session = require('express-session');
const cookieParser = require('cookie-parser');
const app = express.createServer();
`
const twitterConsumerKey = "1";
const twitterConsumerSecret = "2";
const consumer = new oauth.OAuth(
  "https://twitter.com/oauth/request_token",
  "https://twitter.com/oauth/access_token",
  twitterConsumerKey,
  twitterConsumerSecret,
  "1.0A",
  "http://127.0.0.1:8080/sessions/callback",
  "HMAC-SHA1"
);
`
app.use(cookieParser());
app.use(session({
  secret: "very secret"
}));
`
app.use((req, res, next) => {
  res.locals.user = req.session.user;
  next();
});
`
app.get('/sessions/connect', (req, res) =>{
  consumer.getOAuthRequestToken((error, oauthToken, oauthTokenSecret, results) => {
    if (error) {
      res.send(error, 500);
    } else {
      req.session.oauthRequestToken = oauthToken;
      req.session.oauthRequestTokenSecret = oauthTokenSecret;
      res.redirect(`https://twitter.com/oauth/authorize?oauth_token=${req.session.oauthRequestToken}`);
    }
  });
});
`
app.get('/sessions/callback', (req, res) => {
  consumer.getOAuthAccessToken(
    req.session.oauthRequestToken,
    req.session.oauthRequestTokenSecret,
    req.query.oauth_verifier,
    (error, oauthAccessToken, oauthAccessTokenSecret, results) => {
      if (error) {
        res.send('Error', 500);
      } else {
        req.session.oauthAccessToken = oauthAccessToken;
        req.session.oauthAccessTokenSecret = oauthAccessTokenSecret;
        res.redirect('/home');
      }
    });
});
`
app.get('/home', (req, res) => {
  consumer.get(
    "http://twitter.com/account/verify_credentials.json",
    req.session.oauthAccessToken,
    req.session.oauthAccessTokenSecret,
    (error, data, response) => {
      if (error) {
        res.redirect('/sessions/connect');
      } else {
        const parsedData = JSON.parse(data);
        res.send(parsedData.screen_name);
      }
    });
});
`
app.get('*', (req, res) => {
  res.redirect('/home');
});
`
app.listen(8080);

We use the oauth package’s oauth.OAuth constructor to set the keys and secrets required for Twitter OAuth. Those can be gotten from the Twitter developer website. The /sessions/connect route is where we go to log in with Twitter. We call the getOAuthRequestToken to get the OAuth request token. The callback in that method redirects us to the Twitter auth page.

Once we log in, then we’re redirected to the /session/callback route if we set the callback URL to that. It sets the OAuth access token secret and the access token so that we can authenticate with the Twitter API.

Then we when go to the /home route, we authenticate with the access token that we obtained since they’re set as the properties of req.session in the /session/callback route. We used Express session and Cookie Parser to manage the session.

Conclusion

We can do OAuth with Twitter by using the oauth package. Also, we can wait for elements to appear with Puppeteer before proceeding. And we can minimize a window to the system tray and restore it.

Categories
Node.js Tips

Node.js Tips — REPL Tips, Socket.io Connections, and Event Listeners

Like any kind of apps, there are difficult issues to solve when we write Node apps.

In this article, we’ll look at some solutions to common problems when writing Node apps.

Remove Event Listeners in Node.js Event Emitter

We can remove listeners with Node event emitter by using the removeListener method.

For instance, we can write:

const events = require('events');
const EventEmitter = events.EventEmitter;
const rr = new EventEmitter();
const refreshHandler = () => {
  console.log('refresh');
}
rr.on("refresh", refreshHandler);
rr.removeListener("refresh", refreshHandler);

We created our EventEmitter instance.

Then we attach the refreshHandler event listener to the refresh event.

If we don’t need to listen to the refresh event anymore, then we can call removeListener to clear the event listener from memory.

We pass in the event name as the first argument of removeListener .

And the 2nd is the event handler we want to remove.

The Meaning of the “_” (underscore) Symbol in Node.js REPL

The _ symbol returns the result of the last logged expression in the REPL,

For instance, if we typed:

> 2 * 3
6

Then _ returns 6:

> _
6

Access Variable from an exec Callback Function in Node.js

exec takes a callback function with the result of running a shell command.

For instance, we can write:

const execChild = (callback) => {
  const child = exec(cmd, (error, stdout, stderr) => {
    callback(stdout);
  });
}

We get the stdout from the callback, which has the results of our cmd command.

Then we call callback to get the result outside of the exec callback.

We’ve to do this because exec is async, so we won’t know when we’ll get the result.

Now we can use the execChild function by writing:

execChild((result) => console.log(result));

The stdout that we passed into the callback would be the result .

Socket.io —Listen for Connection Event

We can listen for connections on server side.

And we then make connections from the client-side.

On our server-side code, we write:

const io = require('socket.io').listen(8001);
io.sockets.on('connection', (socket) => {
  console.log('user connected');
});

to listen to the connection event, which will be emitted from the client if we call io.connect .

Then in our client-side code, we write:

const socket = io.connect('http://localhost:8001');

to connect to the server we created.

Get a Full File Path in Node.js

We can get the __dirname to get the full path of the current directory.

Then w can write:

const path = require("path");
const fileName = "file.txt";
const fullPath = path.join(__dirname, "/uploads/", fileName);

We use path.join to join all the path segments together in a platform-agnostic way.

Also, we can use the path.resolve method to return the full path to a file.

For instance, we can write:

const path = require("path");
const fullPath = path.resolve("./uploads/file.csv");

We call resolve with the relative path to get the full path of the file.

Get Client’s IP with Express

If we’re writing an Express app, then we can use req.ip to get the client’s IP address.

We can also get the value of the x-forward-for header by writing:

req.headers['x-forwarded-for']

to get the header’s value, which has the IP address.

We can also check req.connection.remoteAddress to do the same thing.

Connect to Socket.Io Server with Specific Path and Namespace

We can connect to a socket.io server with a specific path and namespace by using the of method and the path property.

For instance, we can write:

const io = require('socket.io')(http, {
  path: '/foo/bar'
});

io
  .of('/namespace')
  .on('connection', (socket) => {
    socket.on('message', (data) => {
      io.of('namespace').emit('message', data);
    });
  });

We pass in the http server instance to the socket.io function.

The 2nd argument lets us specify the path with the path property.

Then in the io.of method, we specify the namespace.

The on method lets us watch for connections from clients.

We can listen to events from the client with socket.on .

The first argument is the event name.

The 2nd is a callback that has the data emitted with the event.

Conclusion

We can use removeListener to clear event listeners.

We can connect to a specific path and namespace with socket.io.

It’s easy to get various file and network information with Node.

We can use _ to get the last result that’s returned in the Node REPL.