# Upgrade Guide

# 0.17.0


The migration in this release will create new teams and users_teams tables, and modifies the subscriptions table to be connected to the teams table instead of the users table. To do this, it creates teams and users_teams entries for every entry in users.

This process should be pretty safe and automatic! But if you've made any changes to your users table, there could be unexpected side-effects, so BACK UP YOUR DATABSE BEFORE APPLYING ANY MIGRATIONS FROM THIS RELEASE.

This goes double for any production databases you apply this to.

# New config files

Because of some upgrades made and how webpack works, you will need to create config files in app/config for all config files in wood/config or you will see "Cannot find module ./x" error messages in your browser console. These files can be effectively empty, just inheriting defaults from their "wood" counterparts, like so:

const woodConfig = require('@wood/config/teams');

 * @type {Object} Application teams config values.
module.exports = {
   * Start with default teams config.

  // Overwrite default configs here

# Updating your email configuration

The email transport configuration has been moved to its own file, so that you can import email address configuration values safely from the UI without needing to add email-sending libraries to your UI package, needlessly increasing file size.

To move your configuration, run nodewood eject config-api/email, then move the transportConfig value from your existing config/email entry to config-api/email, ensuring you also move the necessary require statements with it.

# Updating tests

The COOKIE_JWT_USER_ID_1 constant has been renamed to COOKIE_JWT_OWNER_ID_1 to reflect the fact that that it now contains an Owner role. (An COOKIE_JWT_OWNER_ID_1 constant representing a user with a member role has also been added.) You will need to use find and replace to rename this constant through your tests.

# Updating .env file

Add an entry for GENERAL_HASHID_SALT=generalHashidSalt, where generalHashidSalt should be a random series of characters that will be used to salt general-purpose Hashids created in your project. This should be different from your JWT_HASHID_SALT entry so that users cannot examine their JWTs and compare Hashids generated there and elsewhere and potentially glean information about your user IDs or other sequential values.

# ActiveUser

The Users store was renamed to the ActiveUser store for clarity. Now, instead of referencing Users.current alongside Users.role, Users.subscription, and Users.team, you reference ActiveUser.user, ActiveUser.role, ActiveUser.subscription and ActiveUser.team.

You will need to update your Vue files, so that anywhere you were referencing the Users store, you are now referencing the ActiveUser store, and anywhere you were referencing the current property of that store, you are now referencing the user property.

# UserModel

A "teams" attribute was added to UserModel. If you have extended this file, make sure to copy across the additions to the constructor() and toJSON() functions, as well as require TeamModel and RoleModel in your UserModel, as in the copy in wood.

# Configuration

Configuration values are now loaded with the getConfig() function defined in #lib/Config. This allows for easy configuration value overrides when testing. For more information, check the Configuration section of the docs. Your old ways of loading configuration values will still work, but this style is officially deprecated.

For added security, you'll need to move your transportConfig configuration value from config/email.js to config-api/email.js. This is a new folder that has protections to keep it from being loaded in the UI front-end, making it safe to store API back-end configuration secrets. When loading values from files in this folder, you'll need to use the getConfigApi() function defined in #lib/Config. This function works much like getConfig(), except with protections to ensure it cannot be called from within webpack, protecting these files from being included in your UI front-end.

# 0.16.0

# Modifying app/ui/main.js to take into account the new samples feature

Instead of polluting the default main.js with routes for sample files, those files and their routes have been moved to the samples feature in the wood library, which can be cleanly disabled by removing the feature from your app/config/app.js file. Consequently, those routes can be removed from app/ui/main.js, which can be pared down to simply:

import main from '@wood/ui/main';


You will also want to override the wood feature list in app/config/app.js, if you haven't already, and remove the samples feature, like so:

  features: {
    // (Keep your existing "app" features here!)

     * @type {Array<String>} List of enabled Nodewood features.
    wood: [
      // 'samples',

# Add eslint-plugin-promise to your eslintrc.js

The eslint-plugin-promise (opens new window) set of rules are designed to prevent you from making common mistakes with promises and async/await code. These mistakes can lead to especially hard-to-debug errors, so following these eslint rules will make a significant difference in your project.

To enable them, edit your .eslintrc.js file in your application root, and make sure the following two sections look like this:

  extends: [
    // Common

    // Vue
  plugins: [

# Updating your Webpack Dev Server, and enabling HMR

  1. Update the following line in the scripts section of your your root package.json:
    "dev-ui": "cd app && vue-cli-service serve",
  1. If you've created a custom docker-compose.yml file in app/docker, make sure to add the changes from the docker-compose.yml file in wood/docker, specifically the networks and ports section under ui, and the links section under nginx.

  2. If you've created a custom ngingx-default.conf file in app/docker, make sure to add the new location section from the docker-compose.yml file in wood/docker.

  3. If you use anything other than localhost for your local development environment (for example, if you've created a /etc/hosts entry for your Nodewood development), you'll need to put that URL in your devServer.public configuration in app/vue.config.js. For example, if you go to "https://develop.test/app" in your browser, you'll need to modify app/vue.config.js like so:

const config = require('../wood/vue.config.js');

module.exports = {

  devServer: {
    port: 9000,
    public: 'develop.test',
    stats: 'minimal',
    progress: false,
  1. Finally, you'll need to destroy and rebuild your Nginx images:
docker-compose -p PROJECT_NAME -f wood/docker/docker-compose.yml rm -sv nginx
docker-compose -p PROJECT_NAME -f wood/docker/docker-compose.yml up --build --force-recreate --no-deps nginx

Where PROJECT_NAME is the basename of the directory your project lives in. For example, if your project is in ~/projects/myproject, your PROJECT_NAME is myproject. If you have a period in your directory (for example myproject.com), the period is omitted completely for your PROJECT_NAME (myprojectcom). If you use a custom docker-compose.yml, change wood to app in the lines above.

# 0.15.0

# Error message: Cannot find module '@tailwindcss/forms'

You will see this error message during upgrade, as we are upgrading to Tailwind 2, which replaces the @tailwindcss/custom-forms plugin with the @tailwindcss/forms plugin. Since part of the Nodewood upgrade process is trying to apply your chosen Tailwind prefix to all Tailwind classes, this step will break.

Thankfully, it's a pretty easy fix:

  • Complete the Other Packages step below.
  • Run yarn install.
  • If you have set a Tailwind prefix, run nodewood tailwind:prefix and apply your prefix again.

# app/ui/main.js

All the code for this file has been moved to wood/ui/main.js so that Nodewood library updates to this file are automatically applied instead of relying on you to manually update this file. In order to take advantage of this, however, you will need to replace the content of app/ui/main.js with the following:

import main from '@wood/ui/main';
import router from '#ui/router';


  path: '/',
  name: 'home',
  component: () => import(/* webpackChunkName: "home" */ '#ui/pages/HomePage'),

  path: '/about',
  name: 'about',
  component: () => import(/* webpackChunkName: "about" */ '#ui/pages/AboutPage'),
  meta: {
    routeName: 'About',

  path: '/components',
  name: 'components',
  component: () => import(/* webpackChunkName: "components" */ '#ui/pages/ComponentsPage'),
  meta: {
    routeName: 'Components',

You can still import Vue at the beginning of this file and add custom Vue.use directives, etc, before calling main(), and if you have heavily customized this file already, you can leave your customizations in place. However, further updates to main.js may need to be manually applied to your customized file.

# Nginx

Tired of seeing all those OPTIONS logs? This release configures nginx to skip those lines when logging, resulting in cleaner log output. You'll need to rebuild your nginx Docker container to activate this new configuration:

docker-compose -p PROJECT_NAME -f wood/docker/docker-compose.yml build --no-cache nginx

Where PROJECT_NAME is the basename of the directory your project lives in. For example, if your project is in ~/projects/myproject, your PROJECT_NAME is myproject. If you have a period in your directory (for example myproject.com), the period is omitted completely for your PROJECT_NAME (myprojectcom).

# Tailwind 2

Tailwind 2 brings a lot of neat new capabilities, but it may require a little work to get working as expected. To start, you should read the official upgrade guide. (opens new window) Not all of it will be relevant, but it will give you an overview of what's changed.

Nodewood used the @tailwindcss/custom-forms plugin, which has been replaced with the @tailwindcss/forms plugin. This new plugin styles all forms cleanly by default, instead of just all the ones you added form-* classes to. You can safely go and remove all these classes, as they no longer do anything.

Of particular notice is how @apply statement order has changed (opens new window). This may mean that some classes now apply in a different order than you expect, and you'll have to make a small adjustment to that order in order to get your styles working again.

# Other Packages

Many other packages have had their versions updated as well, so you'll want to update the packages in the dependencies section of your app/package.json to the following versions:

    "@vue/cli-plugin-babel": "^4.5.11",
    "@vue/cli-plugin-eslint": "^4.5.11",
    "@vue/cli-plugin-unit-jest": "^4.5.11",
    "@vue/cli-service": "^4.5.11",
    "@vue/eslint-config-airbnb": "^5.3.0",
    "aws-sdk": "^2.839.0",
    "core-js": "^3.8.3",
    "debug": "^4.3.1",
    "enhanced-resolve": "^5.7.0",
    "eslint": "^7.19.0",
    "eslint-config-airbnb-base": "^14.2.1",
    "eslint-plugin-import": "^2.22.1",
    "eslint-plugin-jest": "^24.1.3",
    "eslint-plugin-vue": "^7.5.0",
    "hashids": "^2.2.8",
    "massive": "^6.6.5",
    "moment": "^2.29.1",
    "nodemailer": "^6.4.17",
    "nodemailer-mock": "^1.5.3",
    "pino": "^6.11.1",
    "stripe": "^8.135.0",
    "superagent": "^6.1.0",
    "supertest": "^6.1.3",
    "vue": "2.6.12",
    "vue-js-modal": "^2.0.0-rc.6",
    "vue-router": "^3.5.1",
    "vue-template-compiler": "2.6.12",
    "vuex": "^3.6.2"

You'll also want to update the packages in the devDependencies section of your package.json (in the root of your project) to the following versions:

    "nodemon": "^2.0.7",
    "eslint": "^7.19.0",
    "husky": "^5.0.9",
    "lint-staged": "^10.5.4"

In addition the following packages have been moved to the wood library or removed, and can be removed from all sections of your app/package.json file:


This will update some of the eslint rules that we uses to keep code tidy. Specifically, anywhere that you use a v-slot directive in your Vue components, you'll need to replace it with the # shorthand. Specifically, v-slot:slotname will need to change to #slotname.

# 0.13.0

This release greatly simplifies the specific inclusion of files in the app and wood folders. Instead of prefixing your includes with @wood-config/file or @app-ui/file, you will instead prefix them with @wood/config/file or @app/ui/file. This may seem superficial, but it means that we only have special cases for the wood and app folders, instead of special cases for every folder under each of them. This greatly simplifies the require logic behind the scenes, and means that you can now specifically require any file in the wood and app folders, not just ones with specifically-defined aliases.

Upgrade steps:

  1. Update _moduleAliases in package.json:
  "_moduleAliases": {
    "@app": "./app",
    "@wood": "./wood"
  1. Update moduleNameMapper in jest.config.js:
  moduleNameMapper: {
    '^@app(.*)$': '<rootDir>/app/$1',
    '^@wood(.*)$': '<rootDir>/wood/$1',
  1. Update settings.import/resolver.alias-array.map in .eslintrc.js:
        map: [
          ['#api', [
            resolve(__dirname, 'app/api'),
            resolve(__dirname, 'wood/api'),

          ['#config', [
            resolve(__dirname, 'app/config'),
            resolve(__dirname, 'wood/config'),
          ['#features', [
            resolve(__dirname, 'app/features'),
            resolve(__dirname, 'wood/features'),
          ['#lib', [
            resolve(__dirname, 'app/lib'),
            resolve(__dirname, 'wood/lib'),
          ['#ui', [
            resolve(__dirname, 'app/ui'),
            resolve(__dirname, 'wood/ui'),
          ['@app', resolve(__dirname, 'app')],
          ['@wood', resolve(__dirname, 'wood')],
  1. Update _moduleAliases in app/package.json:
  "_moduleAliases": {
    "@app": ".",
    "@wood": "../wood/"
  1. Replace all instances of @wood-root with @wood.
  2. Replace all instances of @app-root with @app.
  3. Replace all instances of @wood- with @wood/.
  4. Replace all instances of @app- with @app/.

# 0.12.0

This release is a significant change in the file layout of Nodewood. If you are upgrading from < 0.12.0 and do not perform these changes, your app will not work.

This release moves all files in api/src to api, lib/src to lib, and ui/src to ui to better match how the rest of the framework is structured. These files were originally in separate root folders, but were all moved under the wood folder a while back and should properly have had their src folder removed at that time.

But as they say, the best time to plant a tree is twenty years ago, and the second-best time is now.

Upgrade steps:

  1. Move all code from your app/api/src folder to the app/api folder.
  2. Move all code from your app/lib/src folder to the app/lib folder.
  3. Move all code from your app/ui/src folder to the app/ui folder.
  4. Update .eslintrc.js to remove the /src suffix for all entries.
  5. Update jest.config.js to change all instances of src/$1 to $1.
  6. Update package.json to remove the src suffix for all entries in _moduleAliases.
  7. Update package.json to change main to "app/api/api.js".
  8. Update package.json to change scripts.dev-api-only to "cd app/api && NODE_ENV=development nodemon --watch . --watch ../config --watch ../features --watch ../../wood -L ./api.js | npx pino-pretty"
  9. Update app/package.json to remove the src suffix for all entries in _moduleAliases.
  10. Update app/api/api.js and change the startServer line to const { startServer } = require('../../wood/api/api.js');.
  11. Update `app/
  12. Update any other custom code you have added that refers to api/src, lib/src, or ui/src.
  13. Update any other custom code you have added to the api, lib, or ui folders that access files from other folders using relative paths (i.e. if you have a sequence of ../../../, etc).

If you experience into any issues, please contact support at hello@nodewood.com.