npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@siteone/ssr-server

v1.0.0

Published

SSR server for React applications

Downloads

18

Readme

@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.