Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/hiroppy/ssr-sample
A minimum sample of Server-Side-Rendering, Single-Page-Application and Progressive Web App
https://github.com/hiroppy/ssr-sample
apollo babel graphql loadable-components pwa react redux-saga server-side-rendering spa ssr styled-components typescript webpack
Last synced: 3 months ago
JSON representation
A minimum sample of Server-Side-Rendering, Single-Page-Application and Progressive Web App
- Host: GitHub
- URL: https://github.com/hiroppy/ssr-sample
- Owner: hiroppy
- Archived: true
- Created: 2018-07-30T01:46:07.000Z (over 6 years ago)
- Default Branch: master
- Last Pushed: 2021-10-18T21:14:37.000Z (over 3 years ago)
- Last Synced: 2024-08-02T13:35:55.987Z (6 months ago)
- Topics: apollo, babel, graphql, loadable-components, pwa, react, redux-saga, server-side-rendering, spa, ssr, styled-components, typescript, webpack
- Language: TypeScript
- Homepage:
- Size: 6.55 MB
- Stars: 290
- Watchers: 8
- Forks: 38
- Open Issues: 52
-
Metadata Files:
- Readme: README.md
- Funding: .github/FUNDING.yml
Awesome Lists containing this project
README
# A minimum sample of Server-Side-Rendering, Single-Page-Application, and Progressive Web App
## What's this project?
This project introduces how to implement SSR, SPA, and PWA.
### Articles
only Japanese
- [SPA + SSR + PWA の作り方とセキュリティについて](https://blog.hiroppy.me/entry/ssr-sample_2)
- [業務で使える簡単な SSR + SPA のテンプレートを公開した](http://blog.hiroppy.me/entry/ssr-sample)## Feature
This project shows several implementations like below.
- Server Side Rendering
- Single Page Application
- Progressive Web App
- GraphQL
- RESTful API
- Security (CSP, GraphQL)
- Testing
- Infra like the building tools
- Measuring performance## Libraries
### Dependencies
| Name | Purpose | CSR | SSR | Note |
| ------------------- | ---------------------- | --- | --- | ---- |
| react | view | yes | yes | |
| redux | architecure | yes | yes | |
| react-router | routing | yes | yes | |
| react-helmet | head tag | yes | yes | |
| redux-saga | side effects | yes | yes | |
| styled-components | CSS in JS | yes | yes | |
| loadable-components | dynamic import | yes | yes | |
| apollo-boost | GraphQL | yes | yes | |
| express | server side framework | N/A | yes | |
| nanoid | Creating a random hash | N/A | N/A | |### DevDependencies
| Name | Purpose | Note |
| ----------------- | -------------------------------------------- | ---- |
| typescript | Alt | |
| webpack | a bundler for client side | |
| babel | transpile typescript and loadable-components | |
| storybook | preview | |
| storyshots | snapshot tests | |
| jest | test runner | |
| testing-library | a helper to test react | |
| nodemon | a watcher for server side | |
| prettier | formatter | |
| typescript-eslint | linter | |
| workbox | service worker | |
| clinic | performance profiling | |
| autocannon | benchmarking tool | |## Pages
See the router: [src/client/router/](src/client/router/).
This application has 3 pages and creates SPA based on redux and redux-saga.
Saga page and Apollo page use same components so you can compare each implementation.### Top
This page reads README.md using `babel-plugin-macro`.
src: [src/client/components/pages/Top](src/client/components/pages/Top)
### Saga
This page runs just redux-saga application.
page src: [src/client/components/pages/Saga](src/client/components/pages/Saga)
### Apollo
This page runs just apollo application.
page src: [src/client/components/pages/Apollo](src/client/components/pages/Apollo)
## Control SSR and SPA
design concept: [gist](https://gist.github.com/hiroppy/9b5daf8da5cd639a62a917d536f5dfc5)
src: [src/client/sagas/pages.ts](src/client/sagas/pages.ts)All pages fork saga processes.
- `appProcess`
- a common processing to execute on all pages(e.g. confirming login, sending to GA, etc...)
- pages
- `loadTopPage`, `loadingApolloPage`
- just stop saga when it ran at a server
- `loadSagaPage`
- fetching data and then stopping if it ran at a server`appProcess` and `pages` run in parallel, also they run the same code in a server and client.
**Need to call `END` when running on Node.js**
If you do SSR using redux-saga, you have to stop redux-saga process when all processes are finished.
```js
try {
// fetch...yield put(success());
} catch (err) {
yield put(failure(err));
} finally {
if (!process.env.IS_BROWSER) {
yield put(END);
}
}
```## Global Variables
src: [src/server/controllers/renderer/renderer.tsx](src/server/controllers/renderer/renderer.tsx).
Use the following variables to pass data acquired by a server to the client side.
### `data-json`
This script tag has state and data which are fetched via redux-saga, etc at the server.
``.
### `window.__APOLLO_STATE__`
This variable has GraphQL data which are fetched at the server.
## Lighthouse
![lighthouse](./assets/lighthouse.png)
If you want to get 100 point for Best Practices, you need to set a reverse proxy server like Nginx because Express hasn't implemented http/2 yet.(also Performance)
## Setup
```sh
$ git clone [email protected]:hiroppy/ssr-sample.git
$ cd ssr-sample
$ npm i
```## Development
```sh
$ npm start
$ open http://localhost:3000# GraphQL Playground
$ open http://localhost:3000/graphql
```## Storybook
```sh
$ npm run start:storybook
$ open http://localhost:6006
```## Test
```sh
$ npm test
```## Production
```sh
$ npm run build # npm run build:client + npm run build:server
$ npm run start:prod # run server and use 3000
$ open http://localhost:8080
```## Deploy
```sh
$ npm run deploy:storybook
```## Performance
```sh
$ npm run build
$ npm run start:prod
$ npm run benchmark # rpsRunning 10s test @ http://localhost:8080
100 connections┌─────────┬────────┬────────┬────────┬─────────┬───────────┬───────────┬────────────┐
│ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │
├─────────┼────────┼────────┼────────┼─────────┼───────────┼───────────┼────────────┤
│ Latency │ 161 ms │ 406 ms │ 829 ms │ 1277 ms │ 413.26 ms │ 191.69 ms │ 2649.38 ms │
└─────────┴────────┴────────┴────────┴─────────┴───────────┴───────────┴────────────┘
┌───────────┬─────────┬─────────┬─────────┬─────────┬─────────┬────────┬─────────┐
│ Stat │ 1% │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
├───────────┼─────────┼─────────┼─────────┼─────────┼─────────┼────────┼─────────┤
│ Req/Sec │ 210 │ 210 │ 233 │ 264 │ 236.6 │ 18.87 │ 210 │
├───────────┼─────────┼─────────┼─────────┼─────────┼─────────┼────────┼─────────┤
│ Bytes/Sec │ 3.16 MB │ 3.16 MB │ 3.51 MB │ 3.98 MB │ 3.56 MB │ 284 kB │ 3.16 MB │
└───────────┴─────────┴─────────┴─────────┴─────────┴─────────┴────────┴─────────┘Req/Bytes counts sampled once per second.
$ npm run benchmark:flame # flamegraph
```![flamegraph](./assets/flamegraph.png)
## Note
This repository shows how to write and so does not introduce Atomic Design.