@siteone/ssr-server

1.0.0 • Public • Published

@siteone/ssr-server NPM version Dependency Status

SSR server for React Apollo applications

Contents


Features (docs WIP)

  • robust and flexible logging
  • cache management endpoints
  • caching backed by Redis and provided via middleware based on apicache extended to support multi-instance apps

Installation

$ npm install --save @siteone/ssr-server

Service routes

General

Each service route except ping is secured via HTTP Basic auth. Credentials are provided in configuration in section serviceCredentials.

How to programmatically use/consume service endpoints

const request = require('request')

const credentials = 'admin:siteonejenejlepsi' // service credentials
const buff = new Buffer(data)
const authHash = buff.toString('base64') // authorization hash

const options = {
  method: 'GET',
  url: 'http://localhost:3000/__cache/index',
  headers: {
    Authorization: `Basic ${authHash}`,
  },
}
request(options, function(error, response) {
  if (error) throw new Error(error)
  console.log(response.body) // Data from service endpoint
})

Ping /__ping

Responds with plain text string pong. Useful for monitoring response time or whether service is operational.

Metrics /__stats

SSR server exposes endpoint containing various useful metrics for monitoring systems/dashboards.

Sample output:

{
  "server": {
    "status": "up",
    "name": "my-website-on-ssr-server",
    "version": "2.2.2",
    "startedAt": 1579700306454,
    "uptime": 693,
    "env": "development",
    "requests": {
      "total": 1,
      "lastMinute": 0,
      "lastFiveMins": 0,
      "lastFifteenMins": 0
    }
  },
  "node": {
    "version": "v12.14.1",
    "memoryUsage": "82M",
    "uptime": 693.779505301
  },
  "system": {
    "loadavg": [5.06103515625, 7.853515625, 11.1982421875],
    "freeMemory": "72M",
    "hostname": "Viktor-MacBook-Pro.local"
  }
}

Cache

SSR server provides endpoints to display cache index and invalidate both single route or whole cached data.

Current cache index /__cache/index

Sample output:

{
  "all": [
    "/produkty/sporici-ucet/$$appendKey=desktop__walkin__internal__staging",
    "/platby-mobilem-a-hodinkami/$$appendKey=desktop__walkin__internal__staging"
  ],
  "groups": {}
}

Clear whole cache /__cache/clear

Clears whole cache and returns new cache index.

Expected output if clearing was succesful:

{
  "all": [],
  "groups": {}
}

Remove single record from cache /__cache/index/:target

Please note, that target must be URL encoded cache key. Ex: /__cache/clear/%2Fprodukty%2Fsporici-ucet%2F%24%24appendKey%3Ddesktop__walkin__internal__staging. Endpoint response is new cache index.

Example output:

{
  "all": [
    "/platby-mobilem-a-hodinkami/$$appendKey=desktop__walkin__internal__staging"
  ],
  "groups": {}
}

Get cache performance /__cache/performance

Returns cache performance metrics as JSON. Requires trackPerformance option to be enabled. WARNING: super cool feature, but may cause memory overhead issues. Don't use in production.

Example output:

[
    {
        "lastCacheHit": "/test-slamic$$appendKey=unknown",
        "lastCacheMiss": null,
        "callCount": 1,
        "hitCount": 1,
        "missCount": 0,
        "hitRate": 1,
        "hitRateLast100": 1,
        "hitRateLast1000": 1,
        "hitRateLast10000": 1,
        "hitRateLast100000": 1
    }
]

Configuration

SSR server can be configured in many ways via configuration object.

How to structure config

Configuration object is large. Here's example how to compose it in readable way.

Create a config folder next to server entry point. Create new file for each configured topic. Combine them via object spreading in config/index.js.

Server

  • port Number
    • Port number to attach to
    • default 3000
  • host: String
    • Bind to host
    • 0.0.0.0 to be discoverable on network
    • 127.0.0.1 to local discovery only
    • default 0.0.0.0

config/server.js

export default {
  // Server config
  server: {
    port: 3005, // Port
    host: '0.0.0.0', // Host
  },
}

Sentry

  • enabled: Boolean
    • Is Sentry logging enabled
    • default false
  • dsn: String
    • Sentry project DSN
    • default value undefined

sentry.js

export default {
  sentry: {
    enabled: false,
    dsn: 'https://<key>@sentry.siteone.cz/<project>',
  },
}

Cache

  • enabled: Boolean
    • Is caching enabled
    • default false
  • redisClient: optional
    • if provided cache is saved to redis instead of memory
    • required when running multi instance applications
    • initialized redis client
    • default undefined
  • defaultDuration: String|Number
    • Should be either a number (in ms) or a string that can be parsed using ms
    • default 1 hour
  • headerBlacklist: Array
    • List of headers that should never be cached
    • default []
  • statusCodes.exclude: Array
    • List status codes to specifically exclude (e.g. [404, 403] cache all responses unless they had a 404 or 403 status)
    • default []
  • statusCodes.include: Array
    • List status codes to require (e.g. [200] caches ONLY responses with a success/200 code)
    • default []
  • trackPerformance: Boolean
    • enable/disable performance tracking.
    • WARNING: may cause memory overhead issues. Don't use in production.
    • default false
  • headers: Object
    • Header overwrites
    • default {}
// Server side rendering cache
export default {
  ssrCache: {
    debug: false, // If true, enables console output
    defaultDuration: '1 hour', // Should be either a number (in ms) or a string, defaults to 1 hour
    enabled: true, // If false, turns off caching globally (useful on dev)
    redisClient: client,
    headerBlacklist: [], // OPTIONAL: List of headers that should never be cached
    statusCodes: {
      exclude: [], // List status codes to specifically exclude (e.g. [404, 403] cache all responses unless they had a 404 or 403 status)
      include: [200], // List status codes to require (e.g. [200] caches ONLY responses with a success/200 code)
    },
    trackPerformance: false, // Enable/disable performance tracking... WARNING: super cool feature, but may cause memory overhead issues
    headers: {
      //  'cache-control': 'no-cache', // Example of header overwrite
    },
  },
}

Service endpoints credentials

All service endpoints are secured via username and password and verified via HTTP Basic auth header. Set credentials to something secure.

  • user: String
    • username
    • default admin
  • password: String
    • username
    • default siteonejenejlepsi
export default {
  serviceCredentials: {
    user: 'admin',
    password: 'siteonejenejlepsi',
  }
}

Proxies (docs WIP)

export default {
  proxies: [
    // {
    //   method: 'all', // Specify method
    //   route: '/', // Route path, regex or express route format
    //   proxy: {}, // Config object passed to http-proxy-middleware
    // },
  ],
}

Beforewares (docs WIP)

Custom express middlewares registered before main SSR middleware.

export default {
  beforewares: [],
}

Custom routes (docs WIP)

export default {
  routes: [
    // {
    //   method: 'all', // Specify method
    //   route: '/', // Route path, regex or express route format
    //   handler: (req, res, next) => next(), // Express route handler or array of those
    // },
  ],
}

Static files serving (docs WIP)

export default {
  static: {
    root: path.join(process.cwd(), 'public'), // Static files root
    options: {
      immutable: true, // Are files in static folder immutable
      maxAge: ms('10 days'), // Cache max age
    },
  },
}

Logging (docs WIP)

export default {
  logs: {
    transports: [new winston.transports.Console()], // Winston transports
    // Winston log formatter functions
    format: winston.format.combine(
      winston.format.colorize(),
      winston.format.json()
    ),
    meta: false, // OPTIONAL: control whether you want to log the meta data about the request (default to true)
    msg: 'HTTP {{req.method}} {{req.url}}', // OPTIONAL: customize the default logging message. E.g. '{{res.statusCode}} {{req.method}} {{res.responseTime}}ms {{req.url}}'
    expressFormat: true, // Use the default Express/morgan request formatting. Enabling this will override any msg if true. Will only output colors with colorize set to true
    colorize: true, // Color the text and status code, using the Express/morgan color palette (text: gray, status: default green, 3XX cyan, 4XX yellow, 5XX red).
    // ignoreRoute: (req, res) => false, // Function which allows ignoring some routes based on request response data
  },
}

Redirects

SSR server accepts config value redirects which is Array of objects with shape {route: String, target: String, method: String, qsa: Boolean, status: Number}.

route

The parameter route is a string and is required. It can contain parameters like :id, :year([0-9]{4})? or :action(view|edit). It's basically just the same as a route you would pass to app.get().

target

The parameter target is a string and is required. It can contain parameters like :id, :year? or :action(view), where a ? marks an optional parameter and (someString) is a default value.

The parameters get replaced by their respective counterparts in the route.

app.redirect("/a/:id([0-9]+)?", "/b/:id(1)");
app.redirect("/c/:action(view|delete)?", "/d/:action?");
/a           -> /b/1
/a/100       -> /b/100
/c           -> /d
/c/view      -> /d/view

status

The parameter status is an integer and is optional. It is a HTTP (redirection) status code. It defaults to 301.

method

The parameter method is a string and is optional. It is a VERB as in express' router. It defaults to all.

qsa

The parameter qsa is a boolean and is optional. It defaults to true. If set to true, the query string will be appended. By default, it will be discarded.

SSR config (docs WIP)

export default {
  ssr: {
    muiSupport: false, // Add styles collection for material ui
    csrOnly: false, // Use only client side rendering
    applicationConfig: {},
    excludeRoutes: [/pagebuilder/ig, /page-preview/ig], // Routes (array of regexes to exclude from SSR)
    useCsrOnError: true, // Use client side rendering when SSR fails (overriden by csr only)
    apolloClientFactory: () => true, // Factory function which creates configured apollo client
    ApplicationRoot: () => true, // React root component
    configureStore: () => true, // Configure Redux store function
    createInitialState: req => ({}), // OPTIONAL: Function which creates Redux initial state from request data
    bodyExtras: '', // extra markup to put in body
    headExtras: '', // extra markup to put in head
    nojsWarning: 'Zdá se, že máte vypnutý javascript. Zapněte si jej, aby vám tento web fungoval správně.',
    // HTML templates for all response type scenarios
    templates: {
      page: template, // General page where application can be rendered
      unsupportedBrowser: opts =>
        template({ ...opts, prerenderedMarkup: 'Unsupported browser' }), // Template when user is browsing with unsupported browser
      notFound: opts =>
        template({ ...opts, prerenderedMarkup: 'Page not found' }), // Template when page is not found
      error: opts => template({ ...opts, prerenderedMarkup: 'Fatal error' }), // General error template
    },
  }
}

License

MIT © SiteOne, s.r.o.

Readme

Keywords

Package Sidebar

Install

npm i @siteone/ssr-server

Weekly Downloads

2

Version

1.0.0

License

MIT

Unpacked Size

89.5 kB

Total Files

35

Last publish

Collaborators

  • supermartin
  • siteonecz
  • hlavo-siteone
  • viktorbezdek
  • jiri.cerhan