0

I have an application that is served from an express server. I am building all of my templates in vanilla JavaScript without any template files or view engine. I want to pass data that will be available in the client's JavaScript context for normal navigation requests. What is the best practice for doing so?

I am not using AJAX or a single page application at all. All of my templates are fully rendered on the server. Here is a basic version of the server:

const express = require('express')
const app = express()

const meta = `<meta name="viewport" content="width=device-width, initial-scale=1">`
const account = `<button id="sign-out" class="js-authorized">Sign Out</button>`
const template = (title, content) => `<html>
<head>
  <title>${title}</title>

  ${meta}
</head>
<body>
  <div class='account'>
    ${account}
  </div>

  <div class='content'>
    <h1>${title}</h1>

    ${content} 
  </div>
</body>`

app.get('/', (request, response) => {
  const document = template('Index', 'Welcome')

  // How can I send this to the client?
  const data = {
    mainSeed: Math().random(),
    secondarySeeds: [Math().random(), Math().random(), Math().random()]
  }
  // This wouldn't work:
  // return response.send(data, document)

  return response.send(document)
})

I want to ensure that the data will be accessible to any JavaScript on the page, but I don't want to use any templating logic other than JavaScript template literals on the server. What is the best practice for sending data to the client via basic Express?

David Y. Stephenson
  • 872
  • 4
  • 22
  • 44
  • Found this thread but don't know if it's without view engine: https://stackoverflow.com/questions/21172889/express-send-a-page-and-custom-data-to-the-browser-in-a-single-request Hope it'll help – t3__rry Mar 05 '18 at 11:15
  • Thanks for the effort, but looking through the thread, the suggested solution requires that it be an AJAX request (mine are normal navigation requests), and they require a view engine so that the `render` method can be used. – David Y. Stephenson Mar 05 '18 at 11:22

1 Answers1

2

I want to pass data that will be available in the client's JavaScript context for normal navigation requests. What is the best practice for doing so?

By either using a view engine to render your data or limit/force your ajax requests to request application/json so you can respond with JSON.

What you're trying to do is essentially what React, Vue, and Angular do for you already. If you really do not want to use vanilla HTML, then use React or one of the many SPA frameworks/libraries.

What you're trying to accomplish will not work. res.send only accepts one argument: res.send([body]). You can not send anything else with it like you're trying to do. A hack workaround (untested) would be something like this:

const template = (title, content, data) => `<html>
<head>
  <title>${title}</title>

  ${meta}
</head>
<body>
  <div class='account'>
    ${account}
  </div>

  <div class='content'>
    <h1>${title}</h1>

    ${content} 
  </div>
</body>
<script>
  window.data = ${data}
<script>
`

const document = template('Index', 'Welcome', {data: {} })

At this point what makes this any different than using a view engine? It accomplishes the same exact thing. If you were to use a view engine, then you would be able to use res.render:

app.get('/', (request, response) => {
  const data = {
    mainSeed: Math().random(),
    secondarySeeds: [Math().random(), Math().random(), Math().random()]
  }

  response.render('Index', { data, content: 'Welcome' })
})
Cisco
  • 20,972
  • 5
  • 38
  • 60
  • I am not using AJAX or a SPA. My templates are rendered on the server. I want to only use JS strings to build my templates. I don't want to have to use separate files or directories, or have a template syntax other than JS template literals. The only feature I would want from a view engine is including data in the JS page context. Is there such a way to use a view engine to do so while rendering my templates the same way? How do Handlebars or Pug include JS in their context? Could I copy their technique in my templating function? – David Y. Stephenson Mar 05 '18 at 13:17
  • You're making it way harder then it needs to be, but to your question no. View engines (of the ones you mentioned and more) take in the name of the view/file and any data for it and parses it appropriately. You can try to replicate the logic for say `pug` which you can see [here](https://github.com/pugjs/pug/tree/master/packages), but at that point, you may as well use a view engine rather than inventing your own. Only difference is you've compacted it into a single file rather than two. – Cisco Mar 05 '18 at 13:21
  • My long term plan is to create my own view engine, but when I do so, I will need to know the best way to include the JavaScript. I tried looking through the code you linked, but there is a lot, and I have not been able to find a part that clearly shows how JS data is included. – David Y. Stephenson Mar 05 '18 at 13:54
  • The actual package the parses a pug template is [this](https://github.com/pugjs/pug/tree/master/packages/pug-code-gen) one, but as you can see it is quite complex as is many other parts of pug itself. By all means make your own view engine if none can satisfy your needs, but keep in mind if you're going to try and reference other well established engines...you're going to be in for a long night pouring over the verbose source code. – Cisco Mar 05 '18 at 14:35
  • I agree, it's not a good short-term strategy. That's why I'm hoping to learn what best practice pug is basing its implementation on, i.e., where and how server-sent JavaScript data should be included in the page. Hopefully an answer will emerge. I'm also considering using cookies, but of course there are lots of issues there as well. – David Y. Stephenson Mar 05 '18 at 14:47