@webhandle/pages-server
v1.0.0
Published
Express middleware that finds files that match the request path and renders them as templates.
Downloads
52
Readme
@webhandle/pages-server
Express middleware that finds files that match the request path and renders them as templates.
Install
npm install @wehandle/pages-serverUsage
If being used with webhandle:
import setupPages from '@webhandle/pages-server/initialize-webhandle-component.mjs'
setupPagesServerMiddleware(webhandleInstance)If being used with express only:
import {createPagesServerMiddleware} from '@webhandle/pages-server'
let pageServer = createPagesServerMiddleware(pagesLocation)
expressAppOrRouter.use(pageServer)What's Happening
The middleware is looking at the request and trying to determine if there's a file from
the specified folder which would be a match, and if so, loading metadata, running setup
code, and then eventually calling render on the res object.
To determine what's being requested, req.path is used. However, if req.pagePath is set,
that is used instead. This allows previous middleware to control what page gets rendered
without changing the req path.
Finding the Page
Finding a matching page has serveral steps. First, if the request specifies the exact name of
a file (/contact-us.html), then that's used. If the request is more general like
/contact-us then it tries to determine the best match. If there is a file /contact-us.html,
that a good candidate. If /contact-us is a directory on the file system, then it attempts to
find an index file for that directory. It also searches for variants of the file which are a
better match for the requester's language preferences (more on that later).
Loading Metadata
After a specific file is found, the metadata for the page is loaded. The metadata is a file
with the exact same name as the template to be rendered but with the .json extension. This
information is parsed and put in res.locals.page. Metadata is a good place to store things
like the page title, description, or references to database data.
Access Check
After the page metadata is loaded, a check is run to determine if the user can access the page
pageServer.canServePage(req, res, renderSpec). By default, this code only looks to see if
if (res.locals.page.pageVisibility === 'private' && !req.user) {
return false
}That is, if a page is marked as private, it will only be shown if the requester is a logged in user.
Page Prerun Code
It was my observation that lots of pages could be rendered without any conditional logic at all, so long as all dynamic data was loaded and ready for it in advance. This makes creating the code for the pages a relatively simple task of just writing the html and structuring it into templates. This looks cleaner and allows for code separation and reuse.
Each page server has an array of "pre-run" functions with the same signatures as express middleware.
function(req, res, next). They are added by pushing them onto the pageServer.preRun array.
These are called in order, only if a page is about to be rendered, and after the page metadata is loaded. While all of them are always run, it's not expect that they will all do something for every page.
To illustrate how this works, let's say a page has the following metadata:
{
"title": "Contact Us"
, "description": "Meet the team"
, "loadStaffProfiles": true
}A pre-run handler could look like this:
async function(req, res, next) {
if(res.locals.page.loadStaffProfiles) {
// talk to the database to fetch staff profiles
res.locals.staffProfiles = await db.getStaff()
}
next()
}In addition to page specific code, pre-run handlers are a good place to load information which is needed for every page, like a menu, but isn't needed at all unless a page is being rendered.
Headers
pageServer.setHeaders(req, res) is called to set any headers needed before render takes place. By default
it just sets
res.set('Content-Type', 'text/html; charset=UTF-8')Logging
Right before rendering the activity is logged. Pages Server uses filter-log, but you can replace
it with anything you want at:
pageServer.logPageRender = function(req, res, renderSpec) ...Rendering
The final step is rendering done by pageServer.render(req, res, renderSpec) which calls res.render
with the template name.
Doing Page Setup Without a Page
Sometimes you've got everything set up to load info for your pages, and you want all that stuff to run, but you're not really going to render a page. You're going to render some other template or create the response in code. To prep as if you were going to render a page, you can call the async function
pageServer.setupDataForPages(req, res)It's not marked as async, because it usually operates in callback mode, but if you call it with only two arguments, it will return a promise.
Optional Usage
The extensions of what's considered a template can be set in the options. Additionally, the names of the files to be used when a directory is requested can be specified.
import {createPagesServerMiddleware} from '@webhandle/pages-server'
let pageServer = createPagesServerMiddleware(pagesLocation, {
indexNames: ['index']
, templateExtensions: ['pug', 'html']
})
expressAppOrRouter.use(pageServer)Extension Points
server.locator
server.preRun
server.searchAlternates
server.findPageInfo
server.logPageRender
server.canServePage
server.prerenderSetup
server.addHeaders
server.render- preRun - An array of functions that will be run before a page is rendered
- searchAlternatives - A boolean which controls whether language alternatives are used
- locator - An instance of @webhandle/page-locator, or something with the same behavior that finds page candidates
