Code Self Study Forum

[Tutorial] How to Use MySQL or MariaDB with Node.js and Express

Following up on the tutorial on Node/Express servers, this tutorial shows how to connect to MySQL (or MariaDB) with Node.js and Express.

This tutorial assumes that you already know how to create and connect to a MySQL (or MariaDB) database through the command line or another method (PHP, MySQL Workbench, etc.).

Tip: work through the other tutorial first if you aren’t familiar with how Node servers work.

This tutorial will use raw SQL. If you’re looking for a query builder or ORM to use with Node, look into Knex, Bookshelf, Sequelize, or Objection.

I’m using some ES6 syntax in the examples. If anything doesn’t make sense, leave a comment below, and I’ll write another tutorial. :slight_smile:

Initializing the App

Set Up the Database

Make a new project folder for your app and add a file named .gitignore with these contents:

.env
node_modules/

There will be a file named .env that contains your secret password, so you want to be sure that it doesn’t get published to a hosting service like Github or Gitlab. The .gitignore file prevents you from accidentally uploading your passwords to a public server.

Then create a file called .env and put your secret database connection settings in it:

MYSQL_HOST=localhost
MYSQL_USER=your_mysql_username
MYSQL_PASSWORD=your_mysql_password
MYSQL_DB=mysql_node

If you don’t already have a database to experiment with, create a new database named mysql_node and run the following SQL on it:

CREATE DATABASE IF NOT EXISTS mysql_node;
use mysql_node;

CREATE TABLE `monsters` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `name` varchar(100) NOT NULL,
    `hit_points` int(11) NOT NULL,
    `favorite_food` varchar(100) NOT NULL,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4;

INSERT INTO `monsters` VALUES (1,'beholder',189,'halflings'),(2,'dragon',300,'humans'),(3,'goblin',6,'slime');

It’s a simple database with three items, but it should be enough for a quick example:

MariaDB [mysql_node]> select * from monsters;
+----+----------+------------+---------------+
| id | name     | hit_points | favorite_food |
+----+----------+------------+---------------+
|  1 | beholder |        189 | halflings     |
|  2 | dragon   |        300 | humans        |
|  3 | goblin   |          6 | slime         |
+----+----------+------------+---------------+
3 rows in set (0.00 sec)

Install Dependencies

Then generate a package.json file by typing:

$ npm init -y

You can open the package.json file and change any settings you want, but the defaults are fine for this tutorial.

Then install three node packages from NPM:

$ npm install --save mysql dotenv
$ npm install --save-dev nodemon

The mysql and dotenv packages are main dependencies that will always be included in your project, and the nodemon package will only be downloaded in development environments.

What Just Happened

If you look in your project directory, you will see a node_modules directory that holds those three packages, and your package.json file will automatically update with the dependencies. Your file will look a little different, but here is my package.json file for comparison:

{
    "name": "@j127/mysql_node",
    "version": "0.1.0",
    "description": "How to connect to MySQL/MariaDB from Node",
    "main": "app.js",
    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
    },
    "keywords": [],
    "license": "BSD-3-Clause",
    "dependencies": {
        "dotenv": "^8.2.0",
        "mysql": "^2.18.1"
    },
    "devDependencies": {
        "nodemon": "^2.0.3"
    }
}

There will also be a new package-lock.json file there, but you can ignore that file in most cases.

Create the Start Script

Change the scripts section of the package.json file from this:

    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
    },

to this:

    "scripts": {
        "start": "nodemon app.js"
    },

That will let you start your app by typing npm start.

Create the App

Create a file named app.js and put the following code in it:

// This loads the settings from your `.env` file.
require("dotenv").config();

// This makes sure you can read your .env file
const host = process.env.MYSQL_HOST;
const database = process.env.MYSQL_DB;

console.log(`connecting to database ${database} on ${host}`);

You can now try running your app to see if everything is wired together correctly. Type npm start and check for the output of console.log:

node.js mysql/mariadb

That doesn’t connect to the database yet, but just makes sure that your program can read your database settings.

The line that says require("dotenv").config() loads the key-value pairs from your .env file and makes them available as environment variables while the app is running.

In Node you can access environment variables with this syntax:

process.env.NAME_OF_ENVIRONMENT_VARIABLE

So to access the environment variabled named MYSQL_HOST that was defined in your .env file, you can write this in your JavaScript:

const host = process.env.MYSQL_HOST;

Connect to the Database

Once everything above is working, you can try connecting your program to the actual database.

Add a line near the top to import the mysql library that was downloaded with npm. You don’t need to log the environment variables any more, so your app.js file should now look like this:

require("dotenv").config();

// This imports the mysql library
const mysql = require("mysql");

Add some code to connect to the database. The app.js file should look like this:

require("dotenv").config();
const mysql = require("mysql");

// Prepare to connect to MySQL with your secret environment variables
const connection = mysql.createConnection({
    host: process.env.MYSQL,
    user: process.env.MYSQL_USER,
    password: process.env.MYSQL_PASSWORD,
    database: process.env.MYSQL_DB,
});

// Make the connection
connection.connect(function (err) {
    // Check if there is a connection error
    if (err) {
        console.log("connection error", err.stack);
        return;
    }

    // If there was no error, print this message
    console.log(`connected to database`);
});

Run npm start (if it isn’t already running) and you should see the message “connected to database” in your terminal.

Now you can make a query. Here’s an example:

const sql = "SELECT * FROM monsters";
connection.query(sql, function (err, results, fields) {
    if (err) throw err;

    console.log("here are your results", results);
});

connection.end();

After saving the file you should see the data printed in the console something like this:

Reusable Functions

The example below uses JavaScript promises. If you aren’t familiar with promises, they are a kind of thing that “promises” to send you a result (resolve) or an error (reject) whenever it’s done. You can get the result of the promise by attaching .then() to the end. You can get the error by attaching .catch() to the end.

// This loads the settings from your `.env` file.
require("dotenv").config();

// This imports the mysql library
const mysql = require("mysql");

// Prepare to connect to MySQL with your secret environment variables
const connection = mysql.createConnection({
    connectionLimit: 10,
    host: process.env.MYSQL,
    user: process.env.MYSQL_USER,
    password: process.env.MYSQL_PASSWORD,
    database: process.env.MYSQL_DB,
});

// Make the connection
connection.connect(function (err) {
    if (err) {
        console.log("connection error", err.stack);
        return;
    }

    console.log(`connected to database`);
});

// This wraps up the query in a function and returns a promise. It
// "resolves" the results if the database query is okay, and "rejects"
// (errors) if something goes wrong.
function getAllMonsters(limit = 100) {
    return new Promise((resolve, reject) => {
        // This question mark syntax will be explained below.
        const sql = `SELECT * FROM monsters LIMIT ?`;
        connection.query(sql, [limit], function (err, results, fields) {
            if (err) {
                return reject(err);
            }

            return resolve(results);
        });
    });
}

// Run the function using `then` to get the results, and
// `catch` to get any errors.
getAllMonsters()
    .then(data => console.log("all monsters", data))
    .catch(err => console.error(err));

If you run that, the database items should still print out in the console, but now you can reuse the function.

You could write getAllItems(2) to limit the query to 2 results instead of the default 100 results.

Try adding another function at the bottom to get just one monster by its ID:

// WARNING: Pay attention to the question mark syntax
function getMonsterById(id) {
    return new Promise((resolve, reject) => {
        const sql = "SELECT * FROM monsters WHERE id = ?";
        connection.query(sql, [id], function (err, results, fields) {
            if (err) {
                return reject(err);
            }

            return resolve(results);
        });
    });
}

// run the function, looking up a monster by ID
getMonsterById(2)
    .then(data => console.log("monster #2", data))
    .catch(err => console.error(err));

You should see monster #2 printed in the terminal.

This code that gets the data from MySQL is separated from the code that serves webpages, so you can use it in multiple places if you need to, like sending emails or creating local reports. Any JS module that needs to find a monster by ID could just import that function and run getMonsterById(2) to fetch the data.

SECURITY WARNING

The ? syntax in the SQL is a security feature that sanitizes user input. If you don’t write the queries this way, it will open up security holes.

const sql = "SELECT * FROM monsters WHERE id = ?";

The ? gets filled with the next function parameter on the connection.query method. So each item in the array there (just [id] in this example) will fill in one question mark placeholder, sanitizing the input in the process.

const sql = "SELECT * FROM monsters WHERE id = ?";
connection.query(sql, [id], function (err, results, fields) {
// ...

The question mark helps reduce the chance that Little Bobby Tables will visit.

If this part doesn’t make sense, leave a comment below, because it’s important not to miss that for security reasons. :slight_smile:

The version of the code up to this point is on Github as release v0.1.0. You can also find the code above by checking out the simple-node-example branch.

Using Express

Install Express.js in the project with this command:

$ npm install --save express

Create a new file in a new directory at ./db/database.js with these contents:

// This imports the mysql library
const mysql = require("mysql");

const pool = mysql.createPool({
    connectionLimit: 10,
    host: process.env.MYSQL,
    user: process.env.MYSQL_USER,
    password: process.env.MYSQL_PASSWORD,
    database: process.env.MYSQL_DB,
});

It’s similar to the code we wrote before, but this time it uses pool instead of connection. That will let the library manage the database connections.

Then put the code we wrote earlier in the db/database.js file. Here is the file on Github for reference:

Then replace all of the code in the app.js file with a simple Express server.

For example, to create a route at localhost:3333/api that gets all the monsters from the database, you could do this:

app.get("/api", (req, res) => {
    db.getAllMonsters()
        .then(data => res.json(data)) // if successful
        .catch(err => res.status(500).json(err)); // if error
});

To get one monster by its ID at a URL like `localhost:3333/api/2, you could do something like this:

// get a monster by ID (1, 2, or 3)
app.get("/api/:id", (req, res) => {
    // extract the :id parameter from the URL
    const id = req.params.id;

    db.getMonsterById(id)
        .then(data => {
            if (data.length > 0) {
                console.log("data", data);
                res.json(data);
            } else {
                res.status(404).json({ message: "Not Found" });
            }
        })
        .catch(err => res.status(500).json(error));
});

Here is a complete example of an app.js file that also includes a route to search the database by monster name:

You can clone the repo and run it to see a working example. The instructions are in the README.md file.

Learning More

This tutorial is meant to be a quickstart overview. If anything doesn’t make sense or if more examples would help (or if you notice any problems/errors), leave a comment below. :slight_smile:

The docs for the mysql NPM package are also worth checking out.

3 Likes

Another nice tutorial. Is it safe to assume the setup is similar for Express.js and PostgreSQL, or any SQL database? I’m reading different tutorials for Postgre and Express. They’re all slightly different.

1 Like

I think it’s similar in Postgres, but the syntax is a little different. Here are some examples for doing raw SQL queries in postgres and sqlite3.

For a bigger project, I would actually use something like Knex to connect to databases, because it offers database migrations and easier switching of databases.

I’ve heard recommendations for Objection.js (built on top of Knex) but I haven’t tried it. Sequelize is another one.

This tutorial is right on target for me, and is a real eye-opener. I now feel like I know what it means to “build” an API and I can see how keeping all the SQL queries in one place and putting them in functions will help a lot with code maintenance down the road! Awesome!

I see now that you can pass multiple parameters by listing them. For example [id,limit]

I guess to server-side render using the data I would call the database function on a template page like handlebars and render from there?

Thanks for another awesome tutorial. :raised_hands:

1 Like

I think I am getting stuck understanding promises.

Right now I have the idea to call the db function inside the get request for the page and send it as “pageData” to the handlebars template, but I can’t really get the data out of the response.

Here is my code so far

    function nutritionFacts(request, response) {
  //const pageData = { title: 'Nutrition Facts', foodData: apiFoodData }
  db.getDataById(171688,171688,171688)
  .then(data => response.json(data)) // if successful
  .catch(err => response.status(500).json(err)); // if error
 const pageData=JSON.stringify(data);
  console.log(pageData);
  //response.render("./tools/nutrition-facts.hbs", pageData);
}

With a “model, view, controller” (MVC) structure you generally separate your code like described below. There’s an example on Github that I mentioned here.

The Router

This is a file, or group of files, that routes paths to controller functions.

When a request comes in to the server, it first hits the main file (app.js, server.js, or whatever you name it). That main file usually mounts a router or routers in it. Each section of the site can have a different router. Examples of site sections are things like “pages”, “foods”, “api”, etc.

Example of mounting some routers on the app:

// the root path gets sent to the `indexRouter`
app.use("/", indexRouter);
// the `/things` prefix gets sent to the `thingRouter`
app.use("/things", thingRouter); // or `foodRouter`
// the `/api` prefix gets sent to the `apiRouter`
app.use("/api", apiRouter);

The above code designates a router for each section of the site.

Inside the router files, paths in that section of the site are associated with functions from “controller” files.

Example of mapping paths to controller functions:

// import the `controllers/pageController.js` functionality
const ctrl = require("../controllers/pageController");

// Each path on this router calls a different function in the
// `pageController.js` file.
router.get("/", ctrl.homePage);
router.get("/about", ctrl.aboutPage);
router.get("/contact", ctrl.contactPage);

The Controller

A controller (file) guides requests to different functions on the site. It doesn’t define the layout or the data, but only connects the different parts of the site together.

The controller functions have two parameters: request and response.

The request holds information that the user has sent. Other information can be added to the request by middleware after it has arrived on the server, but you can basically think of it has “what the user wants”.

The response has functions on it to send things back to the user, like this:

// send JSON to the user
response.json({ message: "hello world" });

// send a `500` error to the user with JSON
response.status(500).json({ message: "error" });

// send HTML from a template to the user
response.render("templateName", dataObjectToInjectInTheTemplate);

So the controller basically takes in the request, figures out what the user wants, fetches it from other parts of the site (like the “model”, see below), and injects the information into a template before sending the rendered template back with the response.

The Model

The model files are where you can put your functions that query the database. The model files would export functions like getAllMonsters, getMonsterById, etc.

So the controller would get a request for a path like /api/3 (meaning get JSON for monster #3), and it could fetch the data from the model without needed to know how the data is queried or assembled.

// import database functions from the model
const monster = require("./models/monster.js");

// get a monster by ID (1, 2, or 3)
app.get("/api/:id", (req, res) => {
    // extract the :id parameter from the URL
    const id = req.params.id;

    // this function could be defined in `models/monster.js`
    monster.getMonsterById(id)
        .then(data => {
            if (data.length > 0) {
                console.log("data", data);
                res.json(data);
            } else {
                res.status(404).json({ message: "Not Found" });
            }
        })
        .catch(err => res.status(500).json(error));
});

The View

The view refers to files that control the display of the information, in this case Handlebars. Most logic is kept in the controllers, so the view files don’t turn into spaghetti code.

Summary

This is just a general idea, and there are other ways to do it.

  1. Main file – mounts different routers for each section
  2. Router files – map paths in sections to specific controller functions
  3. Controller files – take request and response, do some logic, then render a template or send JSON back to the browser
  4. Model files – the SQL queries go here, and the model files export functions that produce specific data, like getMonsterById that the controllers (and other parts of the site) can use.

If you keep everything separated by function, it makes things easier to change later. If the controller file doesn’t know anything about the data other than it can contain functions like getMonsterById, then you can completely change how that function works under the hood (like change the database type) without needing to rewrite your controllers or other parts of the site that depend on that data.

Other people here might post alternate ideas, but that’s at least how I set it up in my example repo. :slight_smile:

1 Like

I wrote them as one-liners in my example. Here’s another version with more room to write your code.

If the query is successful, the result will be in the data variable. If there was an error it will be in the err variable. As soon as the code does response.json, response.send, or response.render, the response has already been sent back to the browser.

function nutritionFacts(request, response) {
    // you might want to do something like `getDataByIds([171688, 171688, 171688])` here
    db.getDataById(171688, 171688, 171688)
        .then(data => {
            // code here runs if the database query works

            // you can process `data` before sending it back if you want

            // send JSON
            response.json(data);
        })
        .catch(err => {
            // if you want anything to happen when there is an error, put it here

            // this gets sent back if there is an error
            response.status(500).json(err);
        });
    // Don't put anything down here.
}

Here’s another example of how promises work. You can use promises anywhere, but they are often used when you’re doing something asynchronous and your code needs to wait for the result.

// create a promise named `p`
const p = new Promise((resolve, reject) => {

    // `setTimeout` is asynchronous but a promise lets the program wait
    // for a result. After 1.5 seconds, the program will flip a coin to
    // see if there is an error. If there is an error (`1`), it will
    // call `reject` to indicate that there is an error. If the coin
    // flip is `0`, then it will call `resolve` with the OK message.
    setTimeout(() => {
        const coinFlip = Math.floor(Math.random() * 2);

        // "resolve" or "reject" for success or error
        if (coinFlip === 1) {
            reject("ERROR: " + coinFlip);
        } else {
            resolve("OK: " + coinFlip);
        }
    }, 1500);
});


// Then, to get data out of the promise, you use `then` (for `resolve`)
// and `catch` (for `reject`)
p.then(result => {
    // if the promise did a `resolve`, this function will run
    console.log(result);
}).catch(err => {
    // if the promise did a `reject`, this function will run
    console.error(err);
});

Hmm ok, this is interesting. But I don’t want to just send the json. I want to have the json and other data be sent to the handlebars template that will then render the page.

If I just send the json it seems like the same response the API page gives.

But how do I get to using the json data in the page render with handle bars.
Or how do I include it in this render response?
response.render("./tools/nutrition-facts.hbs", pageData);

I will take a closer look at the git repos now too! :slight_smile:
Thanks!

Edit: Really liking the git repo example! Should have got to this sooner! :slight_smile:

Hmm, looks like this might be the solution:


function list(req, res, next) {
    console.log("got here");
    // You could get this data from a database or whatever. I'm using
    // some JSON as a "database".
    axios
        .get(API_BASE_URL + "/api")
        .then(response => {
            console.log(response.data);
            const things = response.data.map(createThing);
            res.render("things", { title: "Things List", things: things });
        })
        .catch(err => res.status(500).end("something broke"));
}

I will also start to change my folder structure to match the way of the MVC. :slight_smile:

Edit: I guess axios is just bundled in express?

1 Like

Hmm ok, I am trying to get data directly from the db…
but when I try to follow the example, I get data undefined in the console, and just an empty json object on the page

function nutritionFacts(req, res) {
    // you might want to do something like `getDataByIds([171688, 171688, 171688])` here
    db.getDataById(171688, 171688, 171688)
    .then(response => {
        console.log("data", response.data);
        const t = response.data;
        const thing = createThing(t);
        console.log(thing);
        res.render("./tools/nutrition-facts.hbs", { title: thing.name3, thing: thing.fdc_id });
    })
        .catch(err => {
            // if you want anything to happen when there is an error, put it here

            // this gets sent back if there is an error
            res.status(500).json(err);
        });
    // Don't put anything down here.
}

You can use either of these two functions to send a response back to the browser:

  • response.json({ message: "hello world" });
  • response.render("templateName", { message: "hello world" });

In either case, your data will probably be contained in a JavaScript object. The first method there will send the object as JSON and the second one will send a rendered template with the object injected into the variable names in the Handlebars template.

JSON vs. JS Objects

Side note: there is a difference between JSON and JavaScript objects. JSON is always a string with a specific kind of JS-like formatting, and JS objects are objects.

When you’re rendering a template, you’re sending the template a JavaScript object. When you want to turn a JS object into JSON, it gets converted to a string. response.json does the conversion for you.

Here’s a quick example of the difference:

// this is JSON (a string with a specific kind of format)
const someJson = '{"message":"a"}';

// this is a JS object, with object-specific methods
const someObj = { message: "b" };

console.log("someJson:", someJson);
console.log("someJson type:", typeof someJson); // string
console.log("=======");

console.log("someObj:", someObj);
console.log("someObj type:", typeof someObj); // object
console.log("=======");

// turn a JSON (string) into a JS object with `JSON.parse`
const jsonConvertedToObject = JSON.parse(someJson);
console.log("now jsonConvertedToObject is:", jsonConvertedToObject);
console.log("jsonConvertedToObject type:", typeof jsonConvertedToObject); // object
console.log("=======");

// convert a JS object to a string (JSON) with `JSON.stringify`
const objectConvertedToJsonString = JSON.stringify(someObj);
console.log("now objectConvertedToJsonString is", objectConvertedToJsonString);
console.log("objectConvertedToJsonString type:", typeof objectConvertedToJsonString); // string

Edit: I extracted that into another tutorial.

So you aren’t actually sending JSON to Handlebars — you’re sending a JS object to Handlebars.

Axios

Axios is an extra library for making HTTP requests. It was only there to simulate a database fetch. It gets data from an API instead of from a database. (Notice that it also uses promises, because of the .then().catch() methods.)

In your case, you could just put the database function there, like:

// get the ID from the URL
const id = request.params.id;

// use the database function
monster.getMonsterById(id) // returns a promise
    .then(data => {
        // do something with the successful query here (called `data`)
        
        // then inject the data into a template or whatever you want to do
        response.render("templateName", data);
    })
    .catch(err => {
        // if there is an error, you can send back a different response

        // you can log the errors however you want
        console.log("there was an error", err);

        // the user might get a generic error message
        const data = {
            message: "Error has been logged. Please try again soon.",
            status: 500,
        };

        // Render an error template
        response.status(500).render("errorTemplate", data);
    });

(There’s a page on error handling here. There may be a better way to do that, but the above code should work in the meantime.)

1 Like

To avoid confusing res with response, I’d rename response to data and try this first:

function nutritionFacts(req, res) {
    db.getDataById(171688, 171688, 171688)
        .then(data => {
            // what prints here?
            console.log("data", data);

            res.render("./tools/nutrition-facts.hbs", {
                title: "Placeholder Title",
                food: "ID",
            });
        })
        .catch(err => {
            // if you want anything to happen when there is an error, put it here

            // this gets sent back if there is an error
            res.status(500).json(err);
        });
    // Don't put anything down here.
}

What prints out for data, and does the template render with the placeholder data?

1 Like

That worked!!! I have been on this for 5 hours now! Thanks! :smiley: :smiley: :smiley:

1 Like

Here is the final solution for posterity. There was an issue since the item was returned as an array and also I did not know how to deconstruct the JSON.

function nutritionFacts(req, res) {
    db.getDataById(171688, 171688, 171688) //DB request with parameters
        .then(data => { 
/*console.log the response. Using [0] since it was an array with one item
but it may have more items, depending on the query*/
            console.log("data", data[0]);
//Getting variables from the response. "Deconstructing" the object.
            const {fdc_id,name3} = data[0];
            console.log(fdc_id);
/*Calling the handlebars template and populating it with variables we
define. The left side "custom, "title" and "food" are what we defined.
 It is good to keep it this way, 
since we might change the database later, 
but the template should stay the same.*/
            res.render("./tools/nutrition-facts.hbs", {
                custom: 'Custom Title',
                title: name3,
                food: fdc_id,
            });
        })
        .catch(err => {
            // if you want anything to happen when there is an error, put it here

            // this gets sent back if there is an error
            res.status(500).json(err);
        });
    // Don't put anything down here.
}

Follow up question, is there a way I can just send every item in the JSON without having to list it?
const {fdc_id,name3} = data[0];
I would guess maybe just
const foodData=data[0];
?

1 Like

It should work to pass in all the foodData to the template, if you want.

const foodData = data[0]; // optionally edit data[0] here.

res.render("./tools/nutrition-facts.hbs", {
    custom: "Custom Title",
    title: name3,
    food: fdc_id,
    foodData: foodData, // now foodData.fdc_id, etc. are available in the template
});

There’s also an ES6 shortcut. If you have an object where the name and the value are the same, you can use just one half.

so this:

{ foodData: foodData }

is identical to this:

{ foodData }

So this syntax would save 10 characters. :slight_smile:

const foodData = data[0]; // optionally edit data[0] here.

res.render("./tools/nutrition-facts.hbs", {
    custom: "Custom Title",
    title: name3,
    food: fdc_id,
    foodData, // shortcut for `foodData: foodData`
});

If you aren’t transpiling your frontend JavaScript then I probably wouldn’t use ES6 on the frontend, but since this JavaScript only runs in Node, you can use ES6 syntax if you want.

1 Like

Awesome! I just checked and this works! :smiley: :smiley: :smiley:
Thanks Josh! :slight_smile:

1 Like

If you want to save a few more characters, you can write this

res.render("tools/nutrition-facts", {

instead of this

res.render("./tools/nutrition-facts.hbs", {

and it will look in your views directory for the hbs file.

It doesn’t really matter though. :slight_smile:

Hmm and here I thought I would overcome my optimization addiction. :wink:
I wonder though, will gulp or webpack fill in the missing hbs?
Or will the lookup time for the code to figure out it is hbs matter? :thinking:
Still good tip. It sounds like it would also work out better should I change template engine.

Question: When I am abstracting the different parts of the code to model, view, and controller, where to I register partials, like:

hbs.registerPartial("header", "{{header}}");

page-controllers.js?

What are you trying to do?

Gulp and Webpack run during development, Handlebars runs during production.

You can use caching to prevent the templates and database queries from running on every page load. When a page loads the first time Node can save it in memory (Redis) and fetch future requests from memory.

I think you can register them right under where you set up Handlebars, in the main app.js file.