Reactium Core

Reactium is a Node/Express + React.js framework for creating front-end apps.

Reactium is built on a core framework. The local development and build configuration that comes out of the box is meant to be upgradeable, so long as your application was built off a semver that is minor-version compatible with the current.

Even for larger version steps, we are going to attempt to describe (or automate) much of the migration from one version of Reactium core to another.

Updating core is performed with the reactium command:

npx reactium update

See: Updating for details

With a number of other front-end frameworks, even those based on React, the philosophy is to entirely hide the local development/build configuration from the developer, sometimes with an eject feature to get raw configuration / build files.

Our philosophy is to create a strong opinion for building a React application, from application structure, to out of the box capabilities such as routing, plugins, and state management, while giving the code maintainer, lead-dev, and ops roles on your team the power to replace, augment, or override behaviors of the build.

Hacking Core

Should you hack reactium_modules? Short answer: no

If you hack on the files in the reactium_modules directory of your project, those changes will be overwritten when executing routing reactium install operations, similar to npm install for node_modules. Instead of modifying these files directly, fork Reactium-Core-Plugins on Github and send us a pull-request. We'll either add your update if appropriate.

That being said, there are a number of build and development overrides built-in to core for your use.

gulp.config.override.js

Exports a function that takes core gulp configuration object as a parameter, and returns configuration object used by gulp tasks.

In some case Webpack uses the gulp config.

/gulp.config.override.js
module.exports = config => {

    // Electron configuration
    config.dest.electron = 'build-electron';
    config.dest.static = 'build-electron/app/public';
    config.electron = {
        config: {
            width: 1280,
            height: 1024,
            show: false,
            title: 'App Title',
            backgroundColor: '#000000',
        },
        devtools: true,
    };

    // Disable auto launch of default browser
    config.open = false;

    return config;
};

gulp.tasks.override.js

Exports a function that takes an object defining core gulp tasks as a parameter, and returns and object whose properties define the tasks run by gulp.

Code example

manifest.config.override.js

Exports a function that takes configuration the manifest-tools use to build src/manifest.js as a parameter, and returns new manifest configuration.

/manifest.config.override.js
module.exports = config => {
    // Disable code splitting for Electron projects
    config.contexts.components.mode = 'sync';
    config.contexts.common.mode = 'sync';
    config.contexts.toolkit.mode = 'sync';
    config.contexts.core.mode = 'sync';

    return config;
};

webpack.override.js

Exports a function that takes the core webpack configuration as a parameter, and returns an object that will be used for webpack to build the js bundle.

/webpack.override.js
const webpack = require('webpack');
const path = require('path');

/**
 * Passed the current webpack configuration from core
 * @param  {Object} webpackConfig the .core webpack configuration
 * @return {Object} your webpack configuration override
 */
module.exports = webpackConfig => {
    const newWebpackConfig = Object.assign({}, webpackConfig);

    newWebpackConfig.entries['entry'] = path.resolve('/path/to/my/entry');
  

    newWebpackConfig.plugins.push(new webpack.ContextReplacementPlugin(/^my-context/, context => {
      context.request = path.resolve('./src/app/my-context');
    }));

    return newWebpackConfig;
};

webpack.umd.override.js

Serves the same purpose at the webpack.override.js except overrides the configuration used for each UMD (Universal Module Definition) module that is created by making a umd.js DDD artifact in your project. The callback function you export will receive at separate webpack configuration for each umd.js module to be created.

/webpack.umd.override.js
/**
 * Passed the current webpack configuration from core
 * @param  {Object} umd module manifest configuration, generated when
 * umd files are located.
 * @param  {Object} webpackConfig the .core webpack configuration
 * @return {Object} your webpack configuration override
 */
module.exports = (umd, webpackConfig) => {
    if (umd.libraryName === 'media-uploader') {
        delete webpackConfig.module.rules;
    }
    return webpackConfig;
};

To see what is generated for each umd in the manifest, look in .tmp/umd-manifest.json after running the build (npm run build). This will contain all the objects generated when looking for umd.js DDD artifacts. See the umd-config.json DDD artifact for more information on controlling this behavior.

babel.config.js

Required and provided by default.

Imports babel configuration and exports the configuration used by Webpack and babel-node.

/babel.config.js
const config = require('./.core/babel.config');

// To add a module resolver for node and webpack
const path = require('path');

const moduleResolver = config.plugins.find(plugin => plugin[0] === 'module-resolver');
moduleResolver[1].alias['redux-addons'] = './src/app/redux-addons';

module.exports = config;

Node/Express

Important to dev-leads, ops and backend devs, there are a number of ways you can change the behavior of the core express server without hacking .core.

Express Middleware

To add or change the stack of Node / Express middleware for the running server, create a src/app/server/middleware.js file, which should export a function taking an array of express middlewares as an argument, and returns the modified list of middlewares.

In this way, you can add/change routing, security configuration, etc to your hearts content.

Express Middleware Example #1:

/src/app/server/middleware.js
module.exports = expressMiddlewares => {

    // Simple Logger
    const mySimpleRequestLogger = (req, res, next) => {
        console.log('SIMPLE LOGGER: REQUEST '+req.path);
        next();
    };

    return [
        {
            name: 'mySimpleRequestLogger',
            use: mySimpleRequestLogger,
        },
        ...expressMiddlewares,
    ];
};

Express Middleware Example #2:

/src/app/server/middleware.js
// Health check route handler

const express = require('express');
const router = express.Router();

const healthy = router.get('/healthcheck', (req, res) => {
    res.status(200).send('ok');
});

module.exports = expressMiddlewares => {
    return [
        {
            name: 'myRouteHandler',
            use: router,
        },
        ...expressMiddlewares,
    ];
};

Express Middleware Example #3:

/src/app/server/middleware.js
// More secure Cross Origin Request Sharing for production:

const cors = require('cors');

module.exports = expressMiddlewares => {
    return expressMiddlewares.map(mw => {
        // no change
        if (nw.name !== 'cors' || !('CORS_ORIGIN' in process.env)) {
            return mw;
        }

        // enforce origin
        return {
            name: 'cors',
            use: cors({
                origin: process.env.CORS_ORIGIN,
            }),
        };
    });
};

Application Defines

Node/Express global.defines variables can be set by creating a src/app/server/defines.js file that exports a JavaScript object.

The file will also be used in constructing a Webpack defines plugin values.

Theoretically, your server-side and FE (front-end) code could make reference to values specified here.

Contrived src/app/server/defines.js:

/src/app/server/defines.js
module.exports = {
    foo: 'bar',
};

Isomorphic Define JS somewhere in front-end React code:

/src/app/server/defines.js
let fooValue;
if (typeof window !== 'undefined') {
    fooValue = foo; // webpack define plugin
} else {
    fooValue = defines.foo; // node express global
}

Environment Variables

Reactium uses the following environment variables:

WEBPACK_RESOURCE_BASE

Use this environment variable when you plan to upload your webpack assets to a CDN. It defaults to serving webpack assets from the document root at /assets/js/. If you wanted to upload the js folder to cdn.example.com for example, you might start the server like so:

WEBPACK_RESOURCE_BASE=https://cdn.example.com/js/ npm start

Default: /assets/js/

PORT

Any TCP port appropriate for HTTP protocol (port.proxy in ./core/gulp.config.js) where running application port is specified by PORT var.

Default: 3030

Some environments like Heroku automatically set this value

APP_PORT

Any TCP port appropriate for HTTP protocol (port.proxy in ./core/gulp.config.js) where running application port is specified by APP_PORT var.

Default: 3030

Some environments automatically set this value

DEBUG

Logging can be helpful for troubleshooting server-side rendering issues.

Server-side logging can drive front-end devs batty

  • off to suppress logging to STDOUT

  • on to enable logging

Default: off

PUBLIC_DIRECTORY

Full-path to public assets directory used the static middleware module.

If you have changed the build process to output static assets (js/css/images, etc) you will want to specify this value.

Default: ./public

ACTINIUM_APP_ID

Used by @atomic-reactor/reactium-api Reactium module, specify the Actinium Application ID used by Actinium API.

Default: Actinium

REST_API_URL

Used by @atomic-reactor/reactium-api Reactium module, specify the fully qualified URL to the Actinium API server.

By default, this URL is proxied from any request on the Reactium host with path prefix /api. e.g. https://myreactiumhost/api proxies requests to https://myactinumhost/api

Default: http://localhost:9000/api

While this default value eases local development, it is an unlikely value for production.

PROXY_ACTINIUM_API

Used by @atomic-reactor/reactium-api Reactium module, can be used to disable the proxy middleware to the Actinium API.

  • on: Actinium API proxy is enabled. API calls direct through Reactium back-end and are proxied to Actinium API.

  • off: Actinium API proxy is disabled. API calls go direct to Actinium API from the Reactium front-end.

Default: on

By proxying through the Reactium back-end, you won't have to worry about CORS.

PROXY_API_PATH

Used by @atomic-reactor/reactium-api Reactium module, can be used to set the front-end path that proxies to the Actinium API.

Default: /api

ACTINIUM_API_ENABLED

Used by @atomic-reactor/reactium-api Reactium module, can be used to disable just the Actinium API.

  • on: Actinium API is enabled

  • off: Actinium API is disabled

Default: on

You can also uninstall the module, using arcli.

Single Page App Template

The default templates are good for simple SPAs, but inevitably you will need to provide a different template for rendering your application's index.html.

To do so, generate server templates using ARCLI:

reactium server template

Modify the newly created templates found in: /src/app/server/template directory

Reactium core will use your custom templates to serve your SPA so long as your template version string satisfies the semver property found for your @atomic-reactor/reactium-core, in your reactiumDependencies section of package.json.

You may need to update these templates after major and minor version updates of core.

To find out the version and semver:

reactium version

Important: replace the version property string found in the SPA templates with the version number found in reactium_modules/@atomic-reactor/reactium-core/reactium-config.js

Static Build Template

There is a /src/index-static.html file provided, which can be used to compile a static index.html file when running static build:

$ npm run static

This is for Reactium applications that will be served by another web-server like Apache, Nginx, Tomcat, etc.

Supports only front-end rendered React

Last updated