Driky / next-right-now

Flexible production-grade boilerplate with Next.js 9 and Zeit Now, with pre-configured Sentry, cookies, Amplitude, Emotion, FontAwesome, GraphQL/GraphCMS (Apollo), Bootstrap (Reactstrap), i18next (Locize), Jest, Cypress (E2E tests) and CI/CD (GH Actions), with full TypeScript support and support for B2B multi-tenants web apps (monorepo)

Home Page:https://nrn-customer1.now.sh/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Unly logo Maintainability Test Coverage Locize Latest version Locize Production version

Next Right Now

Next Right Now (NRN) is meant to be used as a boilerplate for quick getting started with a production-grade project featuring the Next.js framework, hosted on Zeit platform.

NRN is strongly opinionated, but also very flexible. It can also be used as a learning/teaching resource.

A lot of built-in components and utilities are available out of the box. Keep those you like, throw what you dislike, rewrite what doesn't exactly fit your needs, and share what could be useful to others! ;)

Don't hesitate to share your opinion and propose improvements

Overview

This boilerplate is meant for developers with solid React background, who are looking for a way of building production-grade web applications. We took a very special care about the Developer Experience, because it's very important to build quality software.

Knowing Next.js, Zeit and/or GraphQL will be a huge help. We explain how to install them and properly setup them, but it's your job to go deeper and actually learn how they work.

This boilerplate includes must-have built-ins such as i18n, GraphQL, Next.js, TypeScript, monitoring, CI/CD pipeline (dev > staging > production), unit tests, end-to-end tests, analytics and various utilities.

There are a lot of things, and some of them rely on third-party providers. There are so many tools out there and we've chosen them based on our personal opinion (and experience/experiments), while keeping in mind that you may have a different opinion, and may want to use something else.

Thus, you can still use this boilerplate even if you don't like all our choices, because you can simply get rid of what you dislike.

Benefits

  • B2B multi-tenants (AKA "monorepo") first-class support (optional, advanced use-case)
    • Supports configuration, deployment, testing, monitoring of multiple customers through the same project (identical code base, monorepo design)
    • This is a very special consideration, and required quite a lot of efforts to make it works smoothly
      • With multi-tenants setup, we don't use the native Zeit <> Github integration, but our custom Zeit <> Github Actions integration instead (.github)
    • Most projects do not need such capability, but we build our own projects with such requirement in mind, and thus released NRN with such built-in capability
    • It's very easy not to use it if you don't need to, but it'll be a huge time saver for you if you need it!
  • Built-in stages (development, staging, production) workflow
  • TypeScript first-class support
  • GraphQL support (thanks to Apollo, and others)
    • GraphCMS first-class support, which hosts our GraphQL API (server) and database, fully hosted (thanks to GraphCMS1)
    • GraphQL schema available in the developer environment (thanks to GraphQL Config)
  • SSR and CSR capabilities (thanks to the Next.js framework)
  • React hooks over HOC (functional components over classes)
  • Internationalisation (i18n) first-class support (SSR + CSR friendly) (thanks to react-i18next)
  • Testing first-class support
  • Strong observability of the system (monitoring) and push-notification on your own messaging channel (i.e: Slack) when things go wrong (thanks to Sentry1)
  • Universal JS, to re-use code as much as possible between frontend and backend (i.e: universal cookies API)
  • Powerful CSS-in-JS styles, SSR & CSR friendly, JSX-friendly, styled-component friendly (thanks to Emotion)
  • Font first-class support (SSR/CSR friendly, no FOUT effect) (thanks to WebFontLoader)
  • Fine-grained frontend analytics, react-friendly, flexible, SPA-friendly (thanks to Amplitude12)
  • Integrated CI/CD pipeline, automated deployments to preview domains and production domains (thanks to the Zeit, GitHub Actions)
    • Including a dedicated "per-deployment domain", for fast feedback loop and testing means, in an online environment (staging)
    • Including a dedicated "per-branch domain", for fast feedback loop and testing means, in an online environment with a url which is automatically updated as new pushed commits are being deployed (staging)
  • Built-in utilities
    • Convert SVG to TSX components (thanks to SVGR)
    • Font Awesome icons as react components, with SSR support (thanks to Font Awesome)
    • Bootstrap support (thanks to Reactstrap)
    • Node debug mode for the server side (only built-in on WebStorm)
    • NPM security audit (script)
    • NPM developer-friendly outdated packages (script)
    • Display warning on outdated browsers, works even if bundle isn't ES5 compatible (IE11, etc.)
    • Use Zeit secrets for sensitive information
  • Fully documented usage of all our third party NPM libraries (AKA dependencies)

Legend:

  • first-class support: Means that we took a very special care to support this, and that it's not as simple as one may believe
  • 1: Third parties that provide a free plan that is enough for a "simple" application, but make sure to check that their pricing fits you.
  • 2: Beware huge gap between free and paid plans cost.


Overview of Next Right Now

Below are explanations about how NRN works and why we did things the way we did

Tip: If you're interested about how to use this project for your own need, see our "How to use" Guide instead!

Introduction videos

Part 1 - Overview of Next Right Now (15 minutes)

Part 1 - Overview of Next Right Now

Let's talk about why we built RNR in the first place, how it's meant to be used, whom it is for.

This video features Zeit deployments, i18n, GraphCMS, Locize in-context editor, Sentry monitoring, Amplitude analytics, CI/CD Github Actions

Part 2 - Developer Experience with Next Right Now (15 minutes)

Part 2 - Developer Experience with Next Right Now

Let's talk about the developer experience (DX) provided by NRN and how it helps being more efficient.

This video features GraphQL auto-completion and local schema update, deployment workflow, CI/CD Github Actions explanations, interactive E2E testing, GraphsCMS field creation

Guides


Showcases - Live demo

You can see 2 almost identical demo at:

Both share the same source code and configuration, but the database content is different (hosted on GraphCMS).

Tip: You can get metadata at /api/status

Tip: All /api/* are serverless functions, running under AWS Lambda


How to use?

If you're interested about using this project for yourself, see our "How to use" Guide.


Roadmap and future improvements

This boilerplate is usable (and used) in production environments. But there are a few improvements we're really looking towards.

  • Static Site Generation (SSG)
    • This feature is currently in RFC draft and being developed on the Zeit framework. It's gonna be a huge game changer for Next.js, because it will allow statically generated apps that are still dynamic. It's similar of what Gatbsy allows, but it will be much more powerful and flexible, as we will be able to enable SSG on a page basis. This means that on the same app, some page may use SSR, other may use SSG, etc. We are closely watching this RFC, as we hope to change the boilerplate to handle such use case.
  • Admin frontend
    • We are planning on releasing another OSS boilerplate featuring React-admin, which is another OSS framework. The goal is to provide a boilerplate similar to this one, only this time it'll be a BackOffice on top of a GraphQL API. Our first POC was released a few months ago and the next step is to go further. Our secret goal would be to automatically generate all the BackOffice CRUD views dynamically, by using the GraphQL schema (but it seems quite a bit complicated).

Understanding Environments and Stages

The application relies on environment variables to function correctly. Those variables are provided differently depending on the environment.

When working on the development environment (localhost), the variables from .env.build are used by the webpack configuration, also, the now.json configuration file is used (it's a symlink to now.customer1.staging.json), but the variable defined in .env.build take precedence.

When deploying an instance to the Zeit's platform, the variables used are the one that belong to that instance, such as:

In those files, it's the build.env part that is used at build time (build is done on Zeit), which basically replaces all references of every environment variable by the actual value (string replace).

What is an environment?

An environment is "where" the application is running. It can be either "development" (localhost) or "production" (on Zeit's servers).

The environment is defined by the NODE_ENV environment variable.

N.B: It is not possible to any other value, as enforced by Next

When working on your local computer, you automatically use NODE_ENV=developement.

The environment affects how the application is bundled, it is used at build time. (webpack, hot-reloading, etc.)

i.e: In development environment, you have access to PropTypes warnings, but you won't in production.

What is a stage?

A stage is "how" the application is running. It can be either "development" (localhost), "staging" or "production" (on Zeit's servers) - You can add more if you need

The stage is defined by the APP_STAGE environment variable.

N.B: You can use any stage name you like, there is no restriction.

  • When working on your local computer, you automatically use APP_STAGE=developement.
  • When creating a Zeit preview deployment (i.e: when pushing a commit/branch (CD), when using yarn deploy, etc.), you automatically use APP_STAGE=staging.
  • When creating a Zeit production deployment (i.e: when using yarn deploy:customer1:production, when merging a PR to master, etc.), you automatically use APP_STAGE=production.

The stage changes the behaviour of the application, because we sometimes need the application to behave differently depending on the stage.

i.e: In production stage, the Locize configuration uses the production version. When using another stage, it uses the latest version.

i.e: We don't want to enable the same level of debugging in production environment. For instance, Locize is configured to be in debug mode only in non-production stages.


I18n (Internationalization)

The content displayed on NRN is translated using different ways, depending on where the translations are stored:

  • GraphCMS - Dynamic content (fetched from the DB, through GraphCMS API). This content can be updated through GraphCMS backoffice.
  • Locize - Static content (fetched from Locize API). This content can be updated through Locize backoffice, or when using in-context editor.

Fetching translations through GraphCMS

When the content is fetched through GraphCMS, the content is automatically internationalized using the gcms-locale HTTP header provided in the HTTP request.

This means that a given user will not fetch the same content (even though the GraphQL query is identical) based on the gcms-locale value.

The gcms-locale value is a string of the form 'FR, EN'. (case matters!) There is two locales defined in this example (there can be more, but we only handle 2 locales at this time in this app).

  • FR is the first and main locale. Content in French will therefore be loaded first.
  • EN is the second and is a fallback locale. If the content is not found in the main locale, then fallback locales are used.

See the official documentation to learn more.

N.B: Even though it is possible to also specify the language per field, our default approach is to translate all content at once based on the header, because it's so much simpler, and handles automated fallback, which is very useful if the content is not defined in the primary requested language.

Fetching translations through Locize provider

When the content we want to display doesn't come from GraphCMS API, then it's considered as a "static" content.

This means that the content is managed by Locize and must be updated manually there.

Check this video to see Locize in action with react-i18next.

Locize translation workflow in-depth

The Locize project contains two different versions:

  • latest: This Locize version is used in any non-production application stage (development, staging). That's where translations get added/updated by translators.
  • production: This Locize version is only used in the production application stages (APP_STAGE=production) (all customers share the same production version in the current setup, for the sake of simplicity)

This separation between the two versions is important and very useful to avoid deploying unapproved changes to the production stage.

In order to update the production version, all changes must go through the latest version. They can therefore be tested during the development phase, then during the staging phase. Once you're ready to ship the content to production, the production version can be updated from the latest version, which will automatically update all customer production stages.

Tip: New i18n keys are added automatically in the development stage, as they are being added to the source code, thanks to the saveMissing option. This can also be a bit boring with HMR, because useless keys may be created while programming.

Locize additional services

Locize provides a few additional services. Some are free, some are paid.

Other additional services

  • One interesting thing is the ability to share part of the project to be translated by a third party using Crowdbased, without sharing the whole project.
  • There are also several paid Translation services, where you can pay people to translate your content.
  • It is also possible to enable Audit, which allows to audit every change to our translations, and keep changes up to 10 years. (expensive)

Discount

Using the coupon code unly-nrn will grant you a 3-month 15% discount on the premium plans.

Fetching data from GraphCMS

We use multiple libraries to fetch data from GraphCMS. GraphCMS provides a GraphQL endpoint, so we use generic libraries to the GraphQL specification like react-apollo.

See full list of dependencies related to GraphCMS

There are several ways of fetching data from a GraphQL API:

  • react-hoc: HOC (High Order Components) can be used with an components (classes, functional), the GraphQL query is described in the function's wrapper, outside of its body. Former way, tend to be deprecated in favor of react-hooks nowadays. List of known issues.
  • Render Props: Never used it, fixes some issues one can encounter with HOC, but hooks are still better.
  • react-hooks: Hooks can only be used with Functional components (not classes), the GraphQL query is described in the function's body.

We used the hooks approach because it's just cleaner and simpler to understand.


Amplitude (Analytics)

Amplitude is used to collect usage metrics (analytics) of the application.

Amplitude is used only on the frontend part of the application. It is composed of two parts:

See the documentation example at react-amplitude to understand how it's meant to be used. We only use react-amplitude to manipulate events.

Known limitation: Amplitude doesn't provide any backend-compatible API. See amplitude/Amplitude-JavaScript#164

Chrome developer debug tool

The amplitude team has released a Chrome plugin to see the events from the browser.

It is a must-have when working with Amplitude. It's very simple to use and quite helpful.


Continuous Integration & Continuous Deployment (CI/CD)

Overview

Every time a commit is pushed to the repository, or a branch is merged, automated actions are triggered.

Those actions are managed through Github Actions

Workflow of our Zeit <> Github Actions integration

Here is how the multiple steps are ordered:

  1. [Event] A commit is pushed, a branch is merged (or on any change made on the remote repository)
  2. [Trigger] Our Github Actions are triggered
    • Either the staging scripts is executed, or the production script, depending on which branch is impacted (see Github Actions <> Zeit integrations)
    • No matter what script (production vs staging) gets executed, those actions are always triggered:
      1. A new Zeit deployment is triggered, which runs our tests first (yarn test:once) (Failing tests will stop the deployment)
      2. Then, the deployment is deployed, and automatically linked to a custom domain which depends on the git branch name (xxx.now.sh)
      3. Then, our 2E2 tests are triggered using Cypress
        • If they fail, artifacts (screenshots, videos) recorded by Cypress are uploaded to Github to help further debug (See example)

In-depth project's dependencies

See README_DEPENDENCIES


Testing

CI tests Workflow

Zeit will automatically run the tests before deploying, as configured in the yarn build command.

If any test fail, the deployment will be aborted. This ensures that any code that doesn't pass the tests never get deployed online.

Once a deployment has been deployed on Zeit, Github Actions will run our E2E tests, to make sure that the app behaves as expected. This can also be considered as an integration tests suite.

Running tests manually (locally)

You can run interactive tests using Jest with yarn test script.

Running E2E tests manually (locally)

You can run interactive E2E tests using Cypress with yarn e2e:open script.

You can also run them non-interactively using yarn e2e:run script.

You may need to run yarn e2e:install script first


Project folders structure

Overview of the project folder and files structure

  • cypress: Folder used by Cypress (E2E), see "Getting started"
  • public: Static files, see "Static file serving
  • src: Source code of the app
    • Components: React components
      • __snapshots__: Folder used by Jest when using Snapshot testing
      • svg: SVG components, imported from src/svg
    • gql: GraphQL queries, partials, etc.
    • hoc: React High Order Components
    • pages: Next.js pages, see "Pages"
    • propTypes: Shared propTypes (for re-usability)
    • svg: Contains both .svg files and their react .tsx version. When a SVG is converted to a TSX component, it should then be copied to src/components/svg to be used within the app.
    • types: Shared types (for re-usability)
      • data: Types that are data-related, basically those that are related to a database record
    • utils: Various utilities

License

MIT


Vulnerability disclosure

See our policy.


Contributors and maintainers

This project is being maintained by:

Special thanks to:

  • [Contributor] Hugo Martin (Demmonius) - Github Actions CI/CD pipeline

[ABOUT UNLY] Unly logo

Unly is a socially responsible company, fighting inequality and facilitating access to higher education. Unly is committed to making education more inclusive, through responsible funding for students.

We provide technological solutions to help students find the necessary funding for their studies.

We proudly participate in many TechForGood initiatives. To support and learn more about our actions to make education accessible, visit :

Tech tips and tricks from our CTO on our Medium page!

#TECHFORGOOD #EDUCATIONFORALL

About

Flexible production-grade boilerplate with Next.js 9 and Zeit Now, with pre-configured Sentry, cookies, Amplitude, Emotion, FontAwesome, GraphQL/GraphCMS (Apollo), Bootstrap (Reactstrap), i18next (Locize), Jest, Cypress (E2E tests) and CI/CD (GH Actions), with full TypeScript support and support for B2B multi-tenants web apps (monorepo)

https://nrn-customer1.now.sh/

License:MIT License


Languages

Language:TypeScript 73.7%Language:JavaScript 25.1%Language:CSS 1.2%