Plug SSR to you SUI SPA.
SSR can be tought to configure and maintain. SSR handles that for you providing:
- SSRaaS Server-Side Rendering as a Service
- Server improvements shared accross projects
npm install @s-ui/ssr --save
Starts a development server.
Usage: sui-ssr-dev [options]
Options:
-L, --link-all [monorepo] Link all packages inside of monorepo multipackage
-l, --link-package [package] Replace each occurrence of this package with an absolute path to this folder (default: [])
-h, --help display help for command
Examples:
$ sui-ssr dev
$ sui-ssr dev --link-package ./domain --link-all ./components
Generate a static version of the server w/out dependencies in the server folder.
Usage: sui-ssr-build [options]
Options:
-C, --clean Remove build folder before create a new one
-V, --verbose Verbose output
-h, --help output usage information
Examples:
$ sui-ssr build
Create a zip file with all assets needed to run the server in any infra.
It will, over parameter, make that the express server run over a username and password in a htpasswd way.
Usage: sui-ssr-archive [options]
Options:
-C, --clean Remove previous zip
-R, --docker-registry <dockerRegistry> Custom registry to be used as a proxy or instead of the Docker Hub registry
-E, --entry-point Relative path to an entry point script to replace the current one -> https://bit.ly/3e4wT8C
-h, --help Output usage information
-A, --auth <username:password> Will build the express definition under authentication htpassword like.
-O, --outputFileName <outputFileName> A string that will be used to set the name of the output filename. Keep in mind that the outputFilename will have the next suffix <outputFileName>-sui-ssr.zip
Examples:
$ sui-ssr archive
$ sui-ssr archive --outputFileName=myFile // output: myFile-sui-ssr.zip
If no outputFileName is provided it will pipe the standard output stream process.stdout
In most scenarios the default configuration of the Dockerfile should be sufficient to start the sui-ssr server. But it is possible that in some more extreme cases, you will need to do some work inside the container before you start the server.
For those extreme cases you can use the --entry-point
option in the archive
command. You have to provide the path to an "executable" file that will do the ENTRYPOINT functions of your container.
Changing this, can be very dangerous and you have to know very well what you are doing, or you can leave the server unusable. Above all, do what you do, make sure you run whatever you get as arguments to the script. Because this will be the default command for the container
Here is an example of a possible script. It deletes a series of Environment Variables using a RegExp, before starting the server. Notice the last line, how we make sure to execute what comes to it by arguments
#!/usr/bin/env sh
FILTER="^FOO_|^BAR_"
for var in $(printenv | grep -E "$FILTER"); do
unset "$var"
done
echo "System Env variables after filter:"
printenv
exec "$@"
If you want release your server to a branch (generate a clean package-lock file and tag) you can use this command:
$ npx sui-ssr release --email bot@email.com --name BotName
To use this command you have to define a GITHUB_TOKEN
env var in your CI server. This token must be associate to the user and email passing by flags to the command
Example of a .travis.yml
:
- Avoid jobs for branch with the format
vX.Y.Z
- Avoid execute the release job if the commit has a tag associated
- Avoid execute the deploy jobs if the commit has not a tag associated
- Avoid a global install
sudo: required
language: node_js
dist: xenial
node_js:
- '10'
before_install:
- npm config set //registry.npmjs.org/:_authToken $NPM_TOKEN
install:
- true
branches:
except:
- /^v\d+\.\d+\.0$/
jobs:
include:
- stage: release
if: branch = master AND NOT type = pull_request
env: NODE_ENV=production
before_install:
- set -e
- 'if [ ! -z $(git tag --points-at $TRAVIS_COMMIT) ]; then travis_terminate; fi'
script:
- npx @s-ui/ssr release --email srv.scms.jarvis@schibsted.com --name J.A.R.V.I.S
- stage: deploy
if: branch = master AND NOT type = pull_request
env: NODE_ENV=development
before_install:
- set -e
- 'if [ -z $(git tag --points-at $TRAVIS_COMMIT) ]; then travis_terminate; fi'
name: 'Deploy dev'
script:
- echo "Esto construye $NODE_ENV con la versión $TRAVIS_TAG ($TRAVIS_COMMIT_MESSAGE)"
- npm install surge
- npm install --only pro
- npm install --only dev
- npm run ssr:deploy:development
- #stage: deploy pro
env: NODE_ENV=production
before_install:
- set -e
- 'if [ -z $(git tag --points-at $TRAVIS_COMMIT) ]; then travis_terminate; fi'
name: 'Deploy pro'
script:
- echo "Esto construye $NODE_ENV con la versión $TRAVIS_TAG ($TRAVIS_COMMIT_MESSAGE)"
- npm install surge
- npm install --only pro
- npm install --only dev
- npm run ssr:deploy:production
If you want that release commit does not trigger a Travis build you could use the flag
--skip-ci
and commit message will have[skip ci]
string. See more information about Travis skipping build docs
It uses the stdout stream so you can do things like:
$ sui-ssr archive > ./myFileNameOrWhatever.zip
- VERBOSE: Print in the console info about the criticalCSS middleware
- CONSOLE: By default the console is disabled if you want to watch your
console.log
set up this env var to true set up this env var to true
$ VERBOSE=true CONSOLE=true node server/index.js
If you want to change the server´s behavior for very specific business operation like handling errors or logging you must use hooks.
To do that define a file src/hooks.js
which will look as follows:
import TYPES from '@s-ui/ssr/hooks-types'
export default {
[TYPES.LOGGING]: (req, res, next) => {
console.log(req.url)
next()
}
}
Here we implement a direct log into the console. Each hook type could be a middleware function or an array of middlewares functions.
You can check which hooks are available in the hooks-types.js file.
There are two default hooks for 404 and 500 errors. both will look for a 404.html or 500.html file in the src folder and show this file. If you dont define this files, you will get a generic error page.
If you need it, you will be able to config several aspects of your server. All your customs config must live under "config.sui-ssr" key in the package.json of your SPA.
For example:
"config": {
"sui-ssr": {
"forceWWW": true
}
}
Configs accepted:
-
forceWWW
(false
): If you set up to true, then when you have a request fromyoursite.com
the server will respond with a 301 towww.yoursite.com
. But any subdomain in the original request will be respected. -
earlyFlush
(true
): Set it to true in favor of TTFB with the potencial risk of returning soft 404s (200 when the page is not found). Set it to false in order to wait for getInitialProps() result (may throw a 404 error or any other error that will be used to define the proper HTTP error code in the response header) before flushing for the first time. -
useLegacyContext
(true
): If you don't want to use the legacy context you have to set this flag tofalse
. If you leave it as default, you'll be still using the legacy context but also the new one in order to be able to migrate your code easily. -
multiSite
(undefined
): Should be an object containing a mapping with an association of hostname or hostname pattern (key as string) and the site name (value) in order to make your server work with more than one public folder. Important! You must set at least adefault
value to enable this feature. See one simple example below:{ "multiSite": { "my-motorcycles.com": "motorcycles", "my-trucks.com": "trucks", "v([0-9]+).my-trucks.com": "trucks", "default": "cars" } }
Once this set is done, if you want to test your server in
localhost
you must run it setting the custom header'X-Serve-Site'
(with the value of your desired site) to the request. If you're a Google Chrome user, you can achieve it by installing the extension ModHeader. -
createStylesFor
(undefined
): Define how the server should manage style imports. When separate styles per page, by default those imports will load css asynchronously but maybe we want that server to add them as an<link>
in app head and be async or not depends on criticalCSS.appStyles
(string
): Define the webpackChunkName of app styles. These styles usually are imported inapp.js
.createPagesStyles
(false
): Define if pages have style imports that should be managed by the server. The server will get these files from theasset-manifest.json
file, which should be created by sui-ssr.
⚠️ createStylesFor
does not works as expected when sui-bundler configuration hasonlyHash
defined totrue
.
-
SSL
(undefined
): This configuration is intended solely for local development. It's useful for conducting local tests that target production endpoints. The inclusion of a certificate can help to avoid CORS issues. The property in question should be assigned an object that adheres to the following structure:{ "SSL": { "development": { "key": "./.ssl/key.pem", // replace this with the path to the certificate key file "crt": "./.ssl/crt.pem" // replace this with the path to the certificate crt file } } }
## Critical CSS
For development you will need start the server with env vars CRITICAL_CSS_HOST
and CRITICAL_CSS_PROTOCOL
to allow to the external service request your current page.
If you have in your package.json the flag criticalCSS: true
but you want to disable it in development. You can use the env var DISABLE_CRITICAL_CSS=true
when you start your server.
This package uses @s-ui/react-head to put custom HTML in your header.
In order to be able to render context providers from the server side that are global to your web application, create a new file called web-app/src/contextProviders.js
that returns an array containing each context {provider, props}
pair. For example:
// src/contextProviders.js
import {AdvertisingProvider} from '@adv-ui/adit-saitama-context-advertising'
export default [
{
provider: AdvertisingProvider,
props: {
site: 'xx',
environment: 'dev'
}
}
]
### Shared context data between server and client
In case you need to share initial client data needed by a context provider, add an getInitialData
to your context provider. It will be injected into the html as window.__INITIAL_CONTEXT_VALUE__[you context key]
If you want you can link packages when you create a new static version of your site. But if you are using sui-bundler
to link packages too. Please be sure to be in sync with the packages linkeds in both tools
For example, you could use a bash command like this:
#!/bin/bash
FLAGS="\
--link-package ../../frontend-ma--uilib-components/components/value/proposition/ \
--link-package ../../frontend-ma--uilib-components/components/banner/carsCampaign \
"
CDN=/ npx sui-bundler build -C $FLAGS && \
npx sui-ssr build -C $FLAGS && \
PORT=5000 node server/index.js
You can define environment variables by creating a yml file called public-env.yml
in your SPA root directory:
API_ENDPOINT: https://api.pre.somedomain.com
SOME_OTHER_ENV_VAR: https://pre.somedomain.com/contact
- Whatever you add in this file will be available in your context factory as
appConfig.envs
param. - This file must not contain secrets as it is meant to be available in both server and client side.
⚠️ And of course, this file is not meant to be versioned.
SUI-SSR allows any kind of redirects (3XX) in server side rendering when combined with SUI-REACT-INITIAL-PROPS. Check out its documentation to get detailed information and an implementation example.
SUI-SSR allows to set an Http-Cookie in server side rendering when combined with SUI-REACT-INITIAL-PROPS. Check out its documentation to get detailed information and an implementation example.
SUI-SSR allows to set a response headers in server side rendering when combined with SUI-REACT-INITIAL-PROPS. Check out its documentation to get detailed information and an implementation example.
If you want, you can use the output of build inside a aws lambda function. To to that we recomend use UP Maybe you want to use a config like this:
{
"name": "[YOUR APP NAME]",
"profile": "[YOUR AWS PROFILE]",
"hooks": {
"prebuild": "rm ./node || true && wget https://s3.eu-west-3.amazonaws.com/nodejs-8.9.4/node && chmod a+x ./node",
"clean": "npx rimraf ./{server,public,node}"
},
"stages": {
"development": {
"proxy": {
"command": "NODE_ENV=development ./node ./server"
}
},
"staging": {
"hooks": {
"build": "NODE_ENV=preproduction sui-bundler build -C && sui-ssr build -C"
},
"proxy": {
"command": "NODE_ENV=preproduction ./node ./server"
}
},
"production": {
"hooks": {
"build": "NODE_ENV=production sui-bundler build -C && sui-ssr build -C"
},
"proxy": {
"command": "NODE_ENV=production ./node ./server"
}
}
},
"proxy": {
"timeout": 5,
"command": "./node ./server"
},
"lambda": {
"memory": 1024
}
}
Stop compiling s-ui/ssr
sui-ssr build
is going to be deprecated in some following major version:
- With that we could delete mime version of package json. Added because webpack does not get correctly mime version and could cause server errors. It is an express dependency.