Article | Lighthouse Funds - Going Retail (Part 1)

Lighthouse Funds - Going Retail (Part 1)

JAMstack with Next.js, Netlify and Prismic CMS

The new Lighthouse Funds site

Who is Lighthouse Funds?

As per their new website, Lighthouse Funds is “the investment manager of the Lighthouse Global Equity Fund. We’re responsible for identifying and arranging the Fund’s investments”.

They’ve been around as an option for wholesale investors since 2013, but as of April 2021 they have “re-packaged that fund for retail investors”.

Abletech had been supporting the old marketing site for Lighthouse Funds since 2015, but we knew this project would be a total rebuild, with new content and requirements such as:

  • Interactive charts

  • A blog

  • A newsletter

  • Downloadable documents and forms

  • Directings users to sign up through InvestNow

We settled on the JAMstack approach, using Next.js to build a statically-generated site, Prismic CMS to serve the blog content, Mailchimp to manage the newsletters, AWS S3 to store the documents and forms, and Netlify to build and deploy the app. This provided many advantages including rapid delivery of a robust solution with low infrastructure setup and maintenance costs.

We also knew there could be further project phases in the future, that might include features such as authentication and user dashboards. Next.js allows us to provide dynamic content rendering through its hybrid approach to static-site generation, server-side rendering, and client-side rendering.

What even is JAMstack?

A term that describes a modern web development architecture based on a set of fundamental web technologies:

  • JavaScript

  • APIs

  • Markup

JAMstack isn’t a specific technology, it’s a specific approach to building apps and websites:

  • Code in a Git repository

  • Continuous Delivery system

  • Microservice APIs/serverless functions to deliver data, processing and personalisation (at build or run time)

Image source: https://twenty-tech.com/jamstack-how-it-can-be-your-next-web-development-architecture/

In a traditional workflow, when the user requests a certain page, the server will receive that request, build the HTML on demand, and serve it back to the user.

In the JAMstack workflow, the HTML is pre-rendered as part of the build process, and is served from a CDN — a Content Delivery Network.

How it all fits together

(Or “How I want it all to fit together”)

Lighthouse Funds site architecture

I’ll delve more into Netlify later on, but its integration with Git providers is a useful and powerful feature. Netlify lets you link a GitHub, GitLab, or Bitbucket repository to a site for continuous deployment. Each time you push to your Git provider, Netlify runs a build with your tool of choice and deploys the result to their CDN.

In our case, when a developer pushes to the "staging" or "main" branches in our Github repository, a build kicks off in Netlify, which produces a bunch of HTML, CSS, and JS files that are pushed to the Netlify Edge CDN.

The build process also deploys three API endpoints (yes, you can write APIs in Next.js!), and a server-side rendered page as Netlify Functions. These are deployed as serverless Lambda functions without the need to create an AWS account, and with function management handled directly within Netlify.

Publishing a post via Prismic CMS also triggers the build process. When a new post is written and published, a webhook will kick off the Netlify build, which will pick up the new post and create a page based on the dynamic routing setup in the Next.js app.

The end goal is to use webhooks to automatically rebuild the site when new prices are uploaded to the bespoke Rails API, and when new documents are added to the S3 bucket that stores the application forms, monthly factsheets, quarterly reports, etc. These last two processes are currently manual, so it will be great to automate them and save developer time.

As the site is a statically-generated site, when a user accesses it via the browser, the browser will communicate with the CDN and serve back the pre-rendered HTML, and then hydrate it with the JS and execute React, making the page interactive. The CDN will also call off to the AWS Lambda functions when necessary.

Next.js: The React Framework for Production

Let’s talk about Next.js!

Some of their key, out-of-the-box features include:

  • Zero configuration — automatic compilation and bundling

  • Hybrid Static Site Generation and Server Side Rendering, you can pre-render pages at build time (SSG) or build them at request time (SSR)

  • Fast refresh — hot module replacement means there is a fast, reliable live-editing development experience

  • File-system routing — every component under the "pages" directory becomes a route

  • API routes — optionally create API endpoints to provide backend functionality

  • Built-in CSS support — component-level styles with CSS modules and Sass

  • Code-splitting and Bundling — optimised bundle splitting algorithm created by the Google Chrome team

It pays to understand the key differences between Server Side Rendering (SSR) and Client Side Rendering (CSR), in order to understand the benefits of pre-rendering, or Static Site Generation (SSG).

Image source: https://www.smashingmagazine.com/2020/07/differences-static-generated-sites-server-side-rendered-apps/

Client Side Rendering works by providing the browser with an index.html file which contains all the CSS and JS bundles to load. Once these files are loaded, the JS code in them can manipulate and render content on the browser using DOM manipulation. The page then becomes visible and interactive.

In Server Side Rendering, when a certain URL path is loaded, the HTML structure is generated server-side, alongside the minimal JS bundle required for that page, and is then provided to the browser. The page is now visible, but not yet interactive. Behind the scenes, the JS code is working to make the page interactive — this process is called hydration.

Next.js Pre-Rendering

Next.js takes an approach called pre-rendering, of which there are two forms:

  • Static Generation: the HTML is generated at build time and will be reused on each request

  • Server-side rendering: the HTML is generated on each request

Image source: https://david-neuman.com/nextjs-pre-rendering/

Let’s look at some code

When you export the async function getStaticProps from a page, Next.js will pre-render this page at build time using the props returned by getStaticProps.

It should be used if:

  • The data required to render the page is available at build time ahead of a user’s request

  • The data comes from a headless CMS

  • The data can be publicly cached (not user-specific)

  • The page must be pre-rendered (for SEO) and be very fast — getStaticProps generates HTML and JSON files, both of which can be cached by a CDN to improve performance

If a page has dynamic routes — blog posts, for example — and uses getStaticProps, it needs to define a list of paths that have to be rendered to HTML at build time.

So, when you export the async function getStaticPaths from a page that uses dynamic routes, Next.js will statically pre-render all the paths returned by getStaticPaths.

export async function getStaticProps({ params, preview = null, previewData = {} }) {
  const { ref } = previewData

  const post = (await Client().getByUID('post', params.uid, ref ? { ref } : null)) || {}

  return {
    props: {
      preview,
      post,
    },
  }
}

export async function getStaticPaths() {
  const documents = await queryRepeatableDocuments(doc => doc.type === 'post')
  return {
    paths: documents.map(doc => `/blog/${doc.uid}`),
    fallback: 'blocking',
  }
}

~/pages/blog/[uid].js

Next.js will statically generate /blog/post/1 and /blog/post/2 at build time using the page component in ~/pages/blog/[uid].js.

Note re. the fallback: blocking option in getStaticPaths. If a specified path isn’t returned, Next.js will server-side render the page on demand, e.g. a blog post preview.

Let’s look at some more code

When you export the async function getServerSideProps from a page, Next.js will pre-render this page on each request using the data returned by getServerSideProps.

  • It should be used only if you need to pre-render a page whose data must be fetched at request time (e.g. user-specific content)

  • Time to first byte will be slower than getStaticProps because the server must compute the result on every request

  • The result cannot be cached by a CDN (without extra configuration)

export async function getServerSideProps() {
  const dataUrl = `${process.env.NEXT_PUBLIC_RAILS_APP_API_ENDPOINT}/investment_option_returns/investment_option_returns_for_past_five_years`
  const comparisonChartData = await fetch(dataUrl).then(async res => {
    if (res.status !== 200) {
      return []
    }

    const data = await res.json()
    return data
  })

  return {
    props: {
      comparisonChartData,
    },
  }
}

~/pages/index.js

This is from the home page, and returns monthly unit price data from the Rails API. It’s currently called at request time because there is no webhook set up to rebuild the app when new data is added via the admin UI.

Up next

In part two of this three-part series, I dive into Prismic — our chosen Headless CMS, the Mailchimp Marketing API that we use to maintain multiple audience lists for newsletters, and how we set up AWS S3 to store downloadable documents.

Message sent
Message could not be sent
|