Proof of concept in an attempt to integrate NestJS, pReact, Vite, and Fastify.
Ideas :
- Serving React apps from NestjS
- Benefits from SSR for the initial page load and have SEO capabilities
- Hydrate the app in the front-end
- Hot-reload of the UI/JSX in the front-end for improved DX
- Vite for front-end build
- Keeping NestJS infrastructure for serving pages & proposing an API
- Session-based Auth instead of JWTs
- Multiple-Page Apps, no need for a client-side Router
- Works with preact but can actually work with any front-end mechanism (Vue, Svelte, Qwik...)
- The
client
folder contains the front-end code - It is built with Vite
- It's not invoked by any of the
src
code - The
client
andsrc
folder are built using different build tools @fastify/vite
relies on convention, but I believe it can be tweaked
Simply run pnpm dev
to start developing.
You can integrate any vite-compliant component in the front-end.
Sass, Stylus, PostCSS, etc.
To render a page, return a Page
object from the controller.
This will trigger the rendering of the page in the front-end.
This page object can pass props to the front-end.
Run pnpm build
to build the app and pnpm start
to start the production server.
You must register a vite app and create a vite.config.js
file.
The structure mandate the root of your app to live in a subfolder of the root, such as client
.
This folder must contain at least three files :
- A client entry file (for example,
entry-client.ts
) that must be invoked by theindex.html
file - A server entry file (for example,
entry-server.ts
) that will be invoked by the server app. - An
index.html
file serving as the template for the app.
It must looks like this :
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title><!-- title --></title>
<!-- hydration -->
</head>
<body>
<div id="root"><!-- element --></div>
<script type="module" src="/entry-client.ts"></script>
</body>
</html>
It contains comments serving as templating points for insertion :
title
contains the title of the page as provided by the Route componenthydration
contains the data required to hydrate the appelement
contains the HTML of the page generated by the server
The client file must re-hydrate the app in the front-end.
The data used to generate the HTML on the server is available under window.__INITIAL_STATE__
.
The server file must generate the HTML of the page.
It receives the same props as the front-end, including the request
and response
objects.
This file MUST export a render
method that is expected and will be called by the server to render the page.
No routing convention is imposed, you can follow your own routing convention. In this example we use a folder-based structure à la NextJS.
A page is the root component rendered on the server and hydrated on the app. It must be exposed as a default export from the file.
It can also include a metadata
function to provide metadata to the page.
This method receives the exact same props as the page itself.
The build process of the front-end is separated between the client and server. So two commands must be run :
vite build -c vite.config.js --outDir dist/client --ssrManifest
to build the clientvite build -c vite.config.js --outDir dist/server --ssr entry-server.ts
to build the server
These commands will build the files under the root directory of your client.
So if your client is inside client
, the result we live in client/dist/client
and client/dist/server
.
This is perfectly normal and expected by the underlying plugin @fastify/vite
.
- Richer metadata system
- Testability