Magento 2 PWA Studio SSR
Google’s own PWA team said serving static content is not important for SEO, because their clawlers are running JavaScript files. Then why bother with SSR (Server Side Rendering)?
It turns out it’s not so simple. The crawler comes in two waves: first, it will crawl the site without JavaScript, then days, or even weeks later, they will slowly crawl your new pages with their Chrome engine to reveal content that’s only available for browsers with JavaScript. This delay is unacceptable for most of the business cases.
There’s another reason to serve at least some static content: microbrowsers. If you share a link on Twitter (grave sin), Twitter will prefetch your link and display a preview card. This microbrowser won’t run any of the JavaScript on the page, so it will display an empty or some general text on the preview card. Not something any shop owner wants when their customers are keenly sharing their products on twitter, right?
Unfortunately, Magento’s PWA Studio does not offer any SSR solution by the time this article is written. But we can ask it nicely to do so!
Venia is using its own Apollo middleware solution, called UPWARD. UPWARD can read and understand a yml configuration that’s also capable to define graphql requests. In short, UPWARD can reach a graphql endpoint, retrieve some data, and render this data in a mustache template. And voila, the static content from a product or CMS page is there to crawl for the “dumb” crawlers.
Here is an example:
appShell:
inline:
status:
when:
- matches: resource.model
pattern: '.'
use: 200
default: 404
body:
engine: mustache
template: resource.template
provide:
model: resource.model
name: resource.name
entityTypeName: resource.entityTypeName
assets: assetManifest
urlResolver: urlResolver
env: env
UPWARD is interpreting the configuration in a lazy manner. It’s fetching data only when it’s required only. It can see the resource
keyword and looking for it in the configuration. So we need to define it:
resource:
when:
- matches: urlResolver.type
pattern: 'CMS_PAGE'
use:
inline:
# See the top-level 'cmsPage' object for details on its resolved data
model: cmsPage
name: cmsPage.title
entityTypeName:
inline: "Page"
template: '../venia-ui/templates/cmspage-shell.mst'
Same way, the lazy loader needs an urlResolver object as well, which is loading data from the urlResolver graphql endpoint:
urlResolverQuery: '../venia-ui/lib/queries/urlResolver.graphql'
urlResolverResult:
url: magentoGQL
query: urlResolverQuery
variables:
inline:
urlKey: request.url.pathname
The urlResolver graphql endpoint will return with CMS_PAGE and an id for the “home” url key. Then we can fetch the CMS page with this part of the config:
cmsPage: cmsPageResult.data.cmsPage
cmsPageResult:
url: magentoGQL
query: '../venia-ui/lib/queries/getCmsPage.graphql'
headers:
resolver: inline
inline:
Store: request.url.country_code
variables:
inline:
onServer: true
id: urlResolver.id
If you wonder why is this call being executed, scroll back a little and see the model: cmsPage
in the resource:
definition. Too much spaghetti for a Friday night? Well, ask JavaScript developers why the boots (JS) are on the table (server).
Then in the cmspage-shell.mst, the following will render some basic meta tags:
{{> templates/open-document}}
{{#model}}
<title>{{title}} - My awesome site</title>
<meta name="title" content="{{meta_title}}">
<meta name="keywords" content="{{meta_keywords}}">
<meta name="description" content="{{meta_description}}">
{{/model}}
{{> templates/open-body }}
{{> templates/default-initial-contents }}
{{> templates/seed-bundles}}
{{> templates/close-document}}
This method can be used to render schema data, or even render the whole page before any JavaScript run, therefore useful to make the page available for crawlers and microbrowsers. These static pages could be cached in Varnish, such eliminating the time needed to render pages on server side.
Comments