SSR Production Export

WARNING

  • You will need to be running on “@quasar/app” v3.2+ to be able to use this feature.
  • This file is used ONLY for your production build and NOT while developing.

Notice that your generated /src-ssr contains a file named production-export.js. This file defines how your SSR webserver is served. You can start listening to a port or provide a handler for your serverless infrastructure to use. It’s up to you.

Whatever this function returns (if anything) will be exported from your built dist/ssr/index.js.

Anatomy

The /src-ssr/production-export.[js|ts] file is a simple JavaScript file which boots up your SSR webserver and defines what your webserver exports (if exporting anything).

// import something here (serverless packages?)

export default ({
  app, port, isReady, ssrHandler,
  resolve, publicPath, folders, render, serve
}) => {
  // something to do with the server "app"
  // return whatever you want your webserver to export (handler for serverless function?)
}

TIP

Remember that whatever this function returns (if anything) will be exported from your built dist/ssr/index.js.

You can wrap the returned function with ssrProductionExport helper to get a better IDE autocomplete experience (Quasar v2.3.1+ required):

import { ssrProductionExport } from 'quasar/wrappers'

export default ssrProductionExport(({
  app, port, isReady, ssrHandler,
  resolve, publicPath, folders, render, serve
}) => {
  // something to do with the server "app"
  // return whatever you want your webserver to export (handler for serverless?)
})

Parameters

We are referring here to the Object received as parameter by the default exported function of the production-export file.

export default ({
  app, port, isReady, ssrHandler,
  resolve, publicPath, folders, render, serve
}) => {

Detailing the Object:

{
  app,     // Expressjs app instance

  port,    // process.env.PORT or quasar.conf > ssr > prodPort

  isReady, // Function to call returning a Promise
           // when app is ready to serve clients

  ssrHandler, // Prebuilt app handler if your serverless service
              // doesn't require a specific way to provide it.
              // Form: ssrHandler (req, res, next)
              // Tip: it uses isReady() under the hood already

  // all of the following are the same as
  // for the SSR middlewares (check its docs page);
  // normally you don't need these here
  // (use a real SSR middleware instead)
  resolve: {
    urlPath(path)
    root(arg1, arg2),
    public(arg1, arg2)
  },
  publicPath, // String
  folders: {
    root,     // String
    public    // String
  },
  render(ssrContext),
  serve: {
    static(path, opts),
    error({ err, req, res })
  }
}

Default content

The following is the default content of /src-ssr/production-export.js when you add SSR support in a Quasar CLI project:

import { ssrProductionExport } from 'quasar/wrappers'

export default ssrProductionExport(({ app, port, isReady }) => {
  return isReady().then(() => {
    app.listen(port, () => {
      console.log('Server listening at port ' + port)
    })
  })
})

Usage

WARNING

  • If you import anything from node_modules, then make sure that the package is specified in package.json > “dependencies” and NOT in “devDependencies”.
  • This is usually not the place to add middlewares (but you can do it). Add middlewares by using the SSR Middlewares instead. You can configure SSR Middlewares to run only for dev or only for production too.

Listen on a port

This is the default option that you get when adding SSR support in a Quasar CLI project. It starts listening on the configured port (process.env.PORT or quasar.conf > ssr > prodPort).

// src-ssr/production-export.[js|ts]

import { ssrProductionExport } from 'quasar/wrappers'

export default ssrProductionExport(({ app, port, isReady }) => {
  // we wait for app to be ready (including running all SSR middlewares)
  return isReady().then(() => {
    // then we start listening on a port
    app.listen(port, () => {
      // we're ready to serve clients
      console.log('Server listening at port ' + port)
    })
  })
})

Serverless

If you have a serverless infrastructure, then you generally need to export a handler instead of starting to listen to a port.

Say that your serverless service requires you to:

module.exports.handler = __your_handler__

Then what you’d need to do is:

// src-ssr/production-export.[js|ts]

import { ssrProductionExport } from 'quasar/wrappers'

export default ssrProductionExport(({ ssrHandler }) => {
  // "ssrHandler" is a prebuilt handler which already
  // waits for all the middlewares to run before serving clients

  // whatever you return here is equivalent to module.exports.<key> = <value>
  return { handler: ssrHandler }
})

Please note that the provided ssrHandler is a Function of form: (req, res, next) => void. Should you require to export a handler of form (event, context, callback) => void then you will most likely want to use the serverless-http package (see below).

Example: serverless-http

// src-ssr/production-export.[js|ts]

import serverless from 'serverless-http'
import { ssrProductionExport } from 'quasar/wrappers'

export default ssrProductionExport(({ ssrHandler }) => {
  return { handler: serverless(ssrHandler) }
})

Example: Firebase function

// src-ssr/production-export.[js|ts]

import * as functions from 'firebase-functions'
import { ssrProductionExport } from 'quasar/wrappers'

export default ssrProductionExport(({ ssrHandler }) => {
  return {
    handler: functions.https.onRequest(ssrHandler)
  }
})