SlideShare a Scribd company logo
Node.js server side
rendering
Kamil Płaczek
Jestem Kamil
JavaScript Developer
Server-side rendering
• Przeglądarka wykonuje zapytanie, serwer zwraca
przygotowany HTML
Server-side rendering
• W kontekście SPA - uruchomienie kodu aplikacji i renderera
po stronie serwera (kod uniwersalny)
+
src/client/index.js
import React from 'react';
import ReactDOM from ‘react-dom';
import {BrowserRouter} from 'react-router-dom';
import App from './app/app.component';
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById('root')
);
początkowy kod - uruchomienie spa
src/server/index.js
import express from 'express';
const app = express();
app.use(express.static('dist'));
app.get('*', (req, res) => {
// doServerSideRenderingPls();
});
app.listen(3000);
początkowy kod - serwer
Problem 1: Routing
• Jak przełożyć routing z przeglądarki na serwer? 🤷
export default class App extends Component {
render() {
return (
<div className="container">
...
<Route exact path="/" component={Home} />
<Route path="/contact" component={Contact} />
</div>
</div>
);
}
}
Problem 1: Routing
• Routing jest uniwersalny.
export default class App extends Component {
render() {
return (
<div className="container">
...
<Route exact path="/" component={Home} />
<Route path="/contact" component={Contact} />
</div>
</div>
);
}
}
src/server/index.js
...
app.get('*', (req, res) => {
const context = {};
const appString = renderToString(
<StaticRouter location={req.url} context={context}>
<App />
</StaticRouter>
);
if (context.url) {
// redirect was done in client-side routing
res.redirect(301, context.url);
} else {
res.send(`
<!DOCTYPE html>
. . .
<body>
<div id="root">${appString}</div>
<script type="text/javascript" src="client.js"></script>
</body>
</html>
`);
}
});
...
server side rendering z routerem
src/server/index.js
...
app.get('*', (req, res) => {
const context = {};
const appString = renderToString(
<StaticRouter location={req.url} context={context}>
<App />
</StaticRouter>
);
if (context.url) {
// redirect was done in client-side routing
res.redirect(301, context.url);
} else {
res.send(`
<!DOCTYPE html>
. . .
<body>
<div id="root">${appString}</div>
<script type="text/javascript" src="client.js"></script>
</body>
</html>
`);
}
});
...
<div class="container" data-reactroot="">
<div>
<div class="navbar"><span class="brand">Taylor Swift</span>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
</div>
<hr/>
<div>
<p>
Taylor Alison Swift (born December 13, 1989) is an
…
</p>
<ul></ul>
</div>
</div>
</div>
server side rendering z routerem
src/server/index.js
...
app.get('*', (req, res) => {
const context = {};
const appString = renderToString(
<StaticRouter location={req.url} context={context}>
<App />
</StaticRouter>
);
if (context.url) {
// redirect was done in client-side routing
res.redirect(301, context.url);
} else {
res.send(`
<!DOCTYPE html>
. . .
<body>
<div id="root">${appString}</div>
<script type="text/javascript" src="client.js"></script>
</body>
</html>
`);
}
});
...
server side rendering z routerem
<div class="container" data-reactroot="">
<div>
<div class="navbar"><span class="brand">Taylor Swift</span>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
</div>
<hr/>
<div>
<p>
Taylor Alison Swift (born December 13, 1989) is an
…
</p>
<ul></ul>
</div>
</div>
</div>
src/server/index.js
...
app.get('*', (req, res) => {
const context = {};
const appString = renderToString(
<StaticRouter location={req.url} context={context}>
<App />
</StaticRouter>
);
if (context.url) {
// redirect was done in client-side routing
res.redirect(301, context.url);
} else {
res.send(`
<!DOCTYPE html>
. . .
<body>
<div id="root">${appString}</div>
<script type="text/javascript" src="client.js"></script>
</body>
</html>
`);
}
});
...
server side rendering z routerem
<div class="container" data-reactroot="">
<div>
<div class="navbar"><span class="brand">Taylor Swift</span>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
</div>
<hr/>
<div>
<p>
Taylor Alison Swift (born December 13, 1989) is an
…
</p>
<ul></ul>
</div>
</div>
</div>
Problem 2: Dane
• Jak wypełnić dokument danymi podczas SSR? 🌅
async loadData() {
const res = await fetch('https://api.imgur.com/3/gallery/r/taylorswift', {
headers: {Authorization: ‘Client-ID shAK3-it-0ff’},
});
...
this.setState({pics});
}
Problem 2: Dane
Server-side render
and return HTML
Fetch JS app code Run app client-side loadData()
Problem 2: Dane
Server-side render
and return HTML
Fetch JS app
code
Run app client-
side
loadData()loadData()
src/server/index.js
...
const getTaytayPics = async () => {
const res = await fetch('https://api.imgur.com/3/gallery/r/taylorswift', {
headers: {Authorization: ‘Client-ID l00K-wH4t-Y0u-m4DE-m3-D0‘},
});
. . .
return pics;
};
...
app.get('*', async (req, res) => {
const context = {};
if (req.url === '/') {
context.pics = await getTaytayPics();
}
const appString = renderToString(
<StaticRouter location={req.url} context={context}>
<App />
</StaticRouter>
);
...
server side rendering + pobieranie danych v1
src/server/index.js
...
const getTaytayPics = async () => {
const res = await fetch('https://api.imgur.com/3/gallery/r/taylorswift', {
headers: {Authorization: ‘Client-ID l00K-wH4t-Y0u-m4DE-m3-D0‘},
});
. . .
return pics;
};
...
app.get('*', async (req, res) => {
const context = {};
if (req.url === '/') {
context.pics = await getTaytayPics();
}
const appString = renderToString(
<StaticRouter location={req.url} context={context}>
<App />
</StaticRouter>
);
...
server side rendering + pobieranie danych v1
src/client/app/home/home.component.js
export class Home extends Component {
. . .
componentWillMount() {
if (this.props.staticContext && this.props.staticContext.pics) {
this.setState({pics: this.props.staticContext.pics})
} else {
this.loadData();
}
}
komponent react - wykorzystanie danych podczas ssr
Problem 2: Dane
Server-side render
and return HTML
Fetch JS app
code
Run app client-
side
loadData()loadData()
src/server/index.js
app.get('*', async (req, res) => {
. . .
if (req.url === '/') {
context.pics = await getTaytayPics();
}
. . .
} else {
res.send(`<!DOCTYPE html>
. . .>
<script>
window.APP_STATE = ${JSON.stringify({pics: context.pics})};
</script>
<script type="text/javascript" src="client.js"></script>
</body>
</html>`);
server side rendering - przekazanie informacji
src/client/app/home/home.component.js
componentWillMount() {
if (this.props.staticContext && this.props.staticContext.pics) {
this.setState({pics: this.props.staticContext.pics});
} else if (window && window.APP_STATE && window.APP_STATE.pics) {
this.setState({pics: window.APP_STATE.pics})
}
else {
this.loadData();
}
}
komponent react - wykorzystanie danych z ssr
src/server/index.js
...
const getTaytayPics = async () => {
const res = await fetch('https://api.imgur.com/3/gallery/r/taylorswift', {
headers: {Authorization: ‘Client-ID l00K-wH4t-Y0u-m4DE-m3-D0‘},
});
. . .
return pics;
};
...
app.get('*', async (req, res) => {
const context = {};
if (req.url === '/') {
context.pics = await getTaytayPics();
}
const appString = renderToString(
<StaticRouter location={req.url} context={context}>
<App />
</StaticRouter>
);
...
DRY?
src/client/app/app.routes.js
import {Home} from './home/home.component';
import {Contact} from './contact/contact.component';
export const routes = [
{
component: Home,
path: '/',
exact: true,
},
{
component: Contact,
path: '/contact',
},
];
refactoring routingu
src/client/app/home/home.component.js
import fetch from ‘isomorphic-fetch';
. . .
static async loadData() {
const res = await fetch('https://api.imgur.com/3/gallery/r/taylorswift', {
headers: {Authorization: 'Client-ID w1ld3est-dr3am5‘},
});
return pics;
}
. . .
}
refactoring komponentu react
src/server/index.js
import {StaticRouter, matchPath} from 'react-router';
import {routes} from '../client/app/app.routes';
. . .
app.get('*', async (req, res) => {
. . .
const matchedRoute = routes.find(route => matchPath(req.path, route));
if (matchedRoute) {
if (matchedRoute.component && matchedRoute.component.loadData) {
context.data = await matchedRoute.component.loadData();
}
} else {
return res.sendStatus(404);
}
const appString = renderToString(
. . .
refactoring pobierania danych na serwerze
Problem 2.5: Dane
• Jak wypełnić danymi store?
src/client/app/redux/taytay/taytay.actions.js
import fetch from 'isomorphic-fetch';
export const fetchPics = () => async dispatch => {
const res = await fetch('https://api.imgur.com/3/gallery/r/
taylorswift', {
headers: {Authorization: 'Client-ID 0447601918a7bb5'},
});
. . .
return dispatch(setPics(pics));
};
export const setPics = pics => ({. . .});
przeniesienie pobierania danych do akcji
src/client/app/home/home.component.js
import {fetchPics} from '../redux/taytay/taytay.actions';
export class Home extends Component {
static loadData = store => {
return store.dispatch(fetchPics());
};
. . .
wykorzystanie akcji w komponencie
src/client/index.js
import {createAppStore} from './create-store';
const store = createAppStore(window.APP_STATE || {});
ReactDOM.hydrate(
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>,
document.getElementById('root')
);
stworzenie store + odtworzenie stanu z ssr
src/server/index.js
import {createAppStore} from '../client/create-store';
. . .
app.get('*', async (req, res) => {
const store = createAppStore({});
const matchedRoute = routes.find(route => matchPath(req.path, route));
if (matchedRoute) {
if (matchedRoute.component && matchedRoute.component.loadData) {
await matchedRoute.component.loadData(store);
}
. . .
} else {
const state = store.getState();
const html = `<!DOCTYPE html>
. . .
<div id="root">${appString}</div>
<script>
window.APP_STATE = ${JSON.stringify(state)};
</script>
. . .
}
});
stworzenie store + inicjalizacja stanu podczas ssr
src/server/index.js
import {createAppStore} from '../client/create-store';
. . .
app.get('*', async (req, res) => {
const store = createAppStore({});
const matchedRoute = routes.find(route => matchPath(req.path, route));
if (matchedRoute) {
if (matchedRoute.component && matchedRoute.component.loadData) {
await matchedRoute.component.loadData(store);
}
. . .
} else {
const state = store.getState();
const html = `<!DOCTYPE html>
. . .
<div id="root">${appString}</div>
<script>
window.APP_STATE = ${JSON.stringify(state)};
</script>
. . .
}
});
stworzenie store + inicjalizacja stanu podczas ssr
src/server/index.js
import {createAppStore} from '../client/create-store';
. . .
app.get('*', async (req, res) => {
const store = createAppStore({});
const matchedRoute = routes.find(route => matchPath(req.path, route));
if (matchedRoute) {
if (matchedRoute.component && matchedRoute.component.loadData) {
await matchedRoute.component.loadData(store);
}
. . .
} else {
const state = store.getState();
const html = `<!DOCTYPE html>
. . .
<div id="root">${appString}</div>
<script>
window.APP_STATE = ${JSON.stringify(state)};
</script>
. . .
}
});
stworzenie store + inicjalizacja stanu podczas ssr
Problem 3: Wydajność
• Jak szybko będzie działał serwer?
Problem 3: Wydajność
• Jak szybko będzie działał serwer?
src/server/index.js
import cache from 'memory-cache';
. . .
app.use('*', (req, res, next) => {
const cachedHtml = cache.get(req.originalUrl);
if (cachedHtml) {
res.send(cachedHtml);
} else {
next();
}
});
app.get('*', async (req, res) => {
. . .
cache.put(req.path, html);
res.send(html);
}
});
dodanie cache
Problem 3: Wydajność
src/server/index.js
import {createCacheStream} from ‘./cache-stream';
app.get('*', async (req, res) => {
. . .
const cacheStream = createCacheStream(req.path, cache);
cacheStream.pipe(res);
. . .
cacheStream.write(`<!DOCTYPE html>
<html>
. . .
<body>
<div id="root">`);
const appStream = renderToNodeStream(
. . .
);
appStream.pipe(cacheStream, {end: false});
appStream.on('end', () => {
cacheStream.end(`
. . .
</body>
</html>`);
});
}
});
wykorzystanie renderToNodeStream
src/server/cache-stream.js
import {Transform} from 'stream';
export const createCacheStream = (key, cache) => {
const bufferedChunks = [];
return new Transform({
transform(data, enc, cb) {
bufferedChunks.push(data);
cb(null, data);
},
flush(cb) {
cache.put(key, Buffer.concat(bufferedChunks).toString());
cb();
},
});
};
stream pomocniczy - cache
Problem 4: Uwierzytelnianie
• Jak renderować zawartość wymagającą autoryzacji? 🔐
Fetch token from the
server
Save token in
persistent storage
Pass token to the
server on requests
Authenticate &
authorize server-side
Problem 4: Uwierzytelnianie
app.post('/api/login', (req, res) => {
res.json({
token: '... ready for it?',
});
});
src/client/app/redux/auth/auth.actions.js
import fetch from 'isomorphic-fetch';
export const login = () => async dispatch => {
const res = await fetch(API_URL + '/api/login', {
method: 'POST',
});
const auth = await res.json();
localStorage.setItem('taytayAuth', auth.token);
return dispatch(setToken(auth.token));
};
akcja logowania
src/client/app/redux/auth/auth.actions.js
import fetch from 'isomorphic-fetch';
export const login = () => async dispatch => {
const res = await fetch(API_URL + '/api/login', {
method: 'POST',
});
const auth = await res.json();
localStorage.setItem('taytayAuth', auth.token);
return dispatch(setToken(auth.token));
};
akcja logowania
src/client/index.js
. . .
const token = localStorage.getItem('taytayAuth');
const store = createAppStore({
...(window.APP_STATE || {}),
auth: {
token,
},
});
ReactDOM.hydrate(
. . .
);
inicjalizacja store tokenem
src/client/app/redux/auth/auth.actions.js
const withAuthHoc = WrappedComponent => {
return class extends Component {
render() {
return this.props.isAuth ? (
<WrappedComponent {...this.props} />
) : (
<Redirect
to={{
pathname: '/login',
}}
/>
);
}
};
};
prosty guard na route
Problem 4: Uwierzytelnianie
• Jak renderować zawartość wymagającą autoryzacji? 🔐
🍪
src/client/app/redux/auth/auth.actions.js
import fetch from 'isomorphic-fetch';
export const login = () => async dispatch => {
const res = await fetch(API_URL + '/api/login', {
method: 'POST',
});
const auth = await res.json();
Cookies.set('taytayAuth', auth.token, {expires: 7, path: '/'});
return dispatch(setToken(auth.token));
};
zamiana localStorage na cookies
src/client/index.js
. . .
const token = Cookies.get('taytayAuth');
const store = createAppStore({
...(window.APP_STATE || {}),
auth: {
token,
},
});
ReactDOM.hydrate(
. . .
);
zamiana localStorage na cookies
src/server/index.js
import cookieParser from 'cookie-parser';
. . .
app.use(cookieParser());
. . .
app.get('*', async (req, res) => {
const token = req.cookies.taytayAuth;
const store = createAppStore({
auth: {
token,
},
});
if (matchedRoute) {
if (matchedRoute.private && !token) {
return res.redirect(301, ‘/login');
. . .
obsługa cookie na serwerze + inicjalizacja store
src/server/index.js
import cookieParser from 'cookie-parser';
. . .
app.use(cookieParser());
. . .
app.get('*', async (req, res) => {
const token = req.cookies.taytayAuth;
const store = createAppStore({
auth: {
token,
},
});
if (matchedRoute) {
if (matchedRoute.private && !token) {
return res.redirect(301, ‘/login');
. . .
obsługa cookie na serwerze + inicjalizacja store
Podsumowując
✅ Komunikacja z API
✅ Integracja z systemem zarządzania stanem
✅ Cache & streaming
✅ Uwierzytelnianie
Dzięki!
kamil.placzek@tsh.io

github.com/kamilplaczek/taytay-ssr

More Related Content

What's hot

How routing works in angular js
How routing works in angular jsHow routing works in angular js
How routing works in angular js
codeandyou forums
 
Workshop 13: AngularJS Parte II
Workshop 13: AngularJS Parte IIWorkshop 13: AngularJS Parte II
Workshop 13: AngularJS Parte II
Visual Engineering
 
Creating the interfaces of the future with the APIs of today
Creating the interfaces of the future with the APIs of todayCreating the interfaces of the future with the APIs of today
Creating the interfaces of the future with the APIs of todaygerbille
 
Workshop 14: AngularJS Parte III
Workshop 14: AngularJS Parte IIIWorkshop 14: AngularJS Parte III
Workshop 14: AngularJS Parte III
Visual Engineering
 
Introducing Rendr: Run your Backbone.js apps on the client and server
Introducing Rendr: Run your Backbone.js apps on the client and serverIntroducing Rendr: Run your Backbone.js apps on the client and server
Introducing Rendr: Run your Backbone.js apps on the client and serverSpike Brehm
 
Upgrading from Angular 1.x to Angular 2.x
Upgrading from Angular 1.x to Angular 2.xUpgrading from Angular 1.x to Angular 2.x
Upgrading from Angular 1.x to Angular 2.x
Eyal Vardi
 
Workshop 12: AngularJS Parte I
Workshop 12: AngularJS Parte IWorkshop 12: AngularJS Parte I
Workshop 12: AngularJS Parte I
Visual Engineering
 
Rapid prototyping and easy testing with ember cli mirage
Rapid prototyping and easy testing with ember cli mirageRapid prototyping and easy testing with ember cli mirage
Rapid prototyping and easy testing with ember cli mirage
Krzysztof Bialek
 
GHC
GHCGHC
GHC
AidIQ
 
Angular Tutorial Freshers and Experienced
Angular Tutorial Freshers and ExperiencedAngular Tutorial Freshers and Experienced
Angular Tutorial Freshers and Experienced
rajkamaltibacademy
 
Specification-Driven Development of REST APIs by Alexander Zinchuk
Specification-Driven Development of REST APIs by Alexander Zinchuk   Specification-Driven Development of REST APIs by Alexander Zinchuk
Specification-Driven Development of REST APIs by Alexander Zinchuk
OdessaJS Conf
 
Promises are so passé - Tim Perry - Codemotion Milan 2016
Promises are so passé - Tim Perry - Codemotion Milan 2016Promises are so passé - Tim Perry - Codemotion Milan 2016
Promises are so passé - Tim Perry - Codemotion Milan 2016
Codemotion
 
Heroku pop-behind-the-sense
Heroku pop-behind-the-senseHeroku pop-behind-the-sense
Heroku pop-behind-the-sense
Ben Lin
 
Rails 3 overview
Rails 3 overviewRails 3 overview
Rails 3 overviewYehuda Katz
 
Implementing access control with zend framework
Implementing access control with zend frameworkImplementing access control with zend framework
Implementing access control with zend framework
George Mihailov
 
实战Ecos
实战Ecos实战Ecos
实战Ecos
wanglei999
 
How to Build SPA with Vue Router 2.0
How to Build SPA with Vue Router 2.0How to Build SPA with Vue Router 2.0
How to Build SPA with Vue Router 2.0
Takuya Tejima
 
The road to Ember.js 2.0
The road to Ember.js 2.0The road to Ember.js 2.0
The road to Ember.js 2.0
Codemotion
 

What's hot (20)

How routing works in angular js
How routing works in angular jsHow routing works in angular js
How routing works in angular js
 
Workshop 13: AngularJS Parte II
Workshop 13: AngularJS Parte IIWorkshop 13: AngularJS Parte II
Workshop 13: AngularJS Parte II
 
Creating the interfaces of the future with the APIs of today
Creating the interfaces of the future with the APIs of todayCreating the interfaces of the future with the APIs of today
Creating the interfaces of the future with the APIs of today
 
Workshop 14: AngularJS Parte III
Workshop 14: AngularJS Parte IIIWorkshop 14: AngularJS Parte III
Workshop 14: AngularJS Parte III
 
Pyramid REST
Pyramid RESTPyramid REST
Pyramid REST
 
Introducing Rendr: Run your Backbone.js apps on the client and server
Introducing Rendr: Run your Backbone.js apps on the client and serverIntroducing Rendr: Run your Backbone.js apps on the client and server
Introducing Rendr: Run your Backbone.js apps on the client and server
 
Upgrading from Angular 1.x to Angular 2.x
Upgrading from Angular 1.x to Angular 2.xUpgrading from Angular 1.x to Angular 2.x
Upgrading from Angular 1.x to Angular 2.x
 
Workshop 12: AngularJS Parte I
Workshop 12: AngularJS Parte IWorkshop 12: AngularJS Parte I
Workshop 12: AngularJS Parte I
 
Rapid prototyping and easy testing with ember cli mirage
Rapid prototyping and easy testing with ember cli mirageRapid prototyping and easy testing with ember cli mirage
Rapid prototyping and easy testing with ember cli mirage
 
Extend sdk
Extend sdkExtend sdk
Extend sdk
 
GHC
GHCGHC
GHC
 
Angular Tutorial Freshers and Experienced
Angular Tutorial Freshers and ExperiencedAngular Tutorial Freshers and Experienced
Angular Tutorial Freshers and Experienced
 
Specification-Driven Development of REST APIs by Alexander Zinchuk
Specification-Driven Development of REST APIs by Alexander Zinchuk   Specification-Driven Development of REST APIs by Alexander Zinchuk
Specification-Driven Development of REST APIs by Alexander Zinchuk
 
Promises are so passé - Tim Perry - Codemotion Milan 2016
Promises are so passé - Tim Perry - Codemotion Milan 2016Promises are so passé - Tim Perry - Codemotion Milan 2016
Promises are so passé - Tim Perry - Codemotion Milan 2016
 
Heroku pop-behind-the-sense
Heroku pop-behind-the-senseHeroku pop-behind-the-sense
Heroku pop-behind-the-sense
 
Rails 3 overview
Rails 3 overviewRails 3 overview
Rails 3 overview
 
Implementing access control with zend framework
Implementing access control with zend frameworkImplementing access control with zend framework
Implementing access control with zend framework
 
实战Ecos
实战Ecos实战Ecos
实战Ecos
 
How to Build SPA with Vue Router 2.0
How to Build SPA with Vue Router 2.0How to Build SPA with Vue Router 2.0
How to Build SPA with Vue Router 2.0
 
The road to Ember.js 2.0
The road to Ember.js 2.0The road to Ember.js 2.0
The road to Ember.js 2.0
 

Similar to Node.js server-side rendering

Angular server side rendering - Strategies & Technics
Angular server side rendering - Strategies & Technics Angular server side rendering - Strategies & Technics
Angular server side rendering - Strategies & Technics
Eliran Eliassy
 
Reduxing like a pro
Reduxing like a proReduxing like a pro
Reduxing like a pro
Boris Dinkevich
 
Express JS
Express JSExpress JS
Express JS
Alok Guha
 
Maciej Treder ''Angular Universal - a medicine for the Angular + SEO/CDN issu...
Maciej Treder ''Angular Universal - a medicine for the Angular + SEO/CDN issu...Maciej Treder ''Angular Universal - a medicine for the Angular + SEO/CDN issu...
Maciej Treder ''Angular Universal - a medicine for the Angular + SEO/CDN issu...
OdessaJS Conf
 
Building Web Apps with Express
Building Web Apps with ExpressBuilding Web Apps with Express
Building Web Apps with Express
Aaron Stannard
 
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
Igor Bronovskyy
 
React native by example by Vadim Ruban
React native by example by Vadim RubanReact native by example by Vadim Ruban
React native by example by Vadim Ruban
Lohika_Odessa_TechTalks
 
Universal JavaScript Web Applications with React - Luciano Mammino - Codemoti...
Universal JavaScript Web Applications with React - Luciano Mammino - Codemoti...Universal JavaScript Web Applications with React - Luciano Mammino - Codemoti...
Universal JavaScript Web Applications with React - Luciano Mammino - Codemoti...
Codemotion
 
Silex Cheat Sheet
Silex Cheat SheetSilex Cheat Sheet
Silex Cheat Sheet
Andréia Bohner
 
Angular 1.x vs. Angular 2.x
Angular 1.x vs. Angular 2.xAngular 1.x vs. Angular 2.x
Angular 1.x vs. Angular 2.x
Eyal Vardi
 
Node.js: scalability tips - Azure Dev Community Vijayawada
Node.js: scalability tips - Azure Dev Community VijayawadaNode.js: scalability tips - Azure Dev Community Vijayawada
Node.js: scalability tips - Azure Dev Community Vijayawada
Luciano Mammino
 
Advanced redux
Advanced reduxAdvanced redux
Advanced redux
Boris Dinkevich
 
Universal JavaScript
Universal JavaScriptUniversal JavaScript
Universal JavaScript
名辰 洪
 
Building Universal Web Apps with React ForwardJS 2017
Building Universal Web Apps with React ForwardJS 2017Building Universal Web Apps with React ForwardJS 2017
Building Universal Web Apps with React ForwardJS 2017
Elyse Kolker Gordon
 
Restful Web Service
Restful Web ServiceRestful Web Service
Restful Web Service
Bin Cai
 
10 Excellent Ways to Secure Spring Boot Applications - Okta Webinar 2020
10 Excellent Ways to Secure Spring Boot Applications - Okta Webinar 202010 Excellent Ways to Secure Spring Boot Applications - Okta Webinar 2020
10 Excellent Ways to Secure Spring Boot Applications - Okta Webinar 2020
Matt Raible
 
Maciej Treder "Server-side rendering with Angular—be faster and more SEO, CDN...
Maciej Treder "Server-side rendering with Angular—be faster and more SEO, CDN...Maciej Treder "Server-side rendering with Angular—be faster and more SEO, CDN...
Maciej Treder "Server-side rendering with Angular—be faster and more SEO, CDN...
Fwdays
 
Dart Power Tools
Dart Power ToolsDart Power Tools
Dart Power Tools
Matt Norris
 
Building an angular application -1 ( API: Golang, Database: Postgres) v1.0
Building an angular application -1 ( API: Golang, Database: Postgres) v1.0Building an angular application -1 ( API: Golang, Database: Postgres) v1.0
Building an angular application -1 ( API: Golang, Database: Postgres) v1.0
Frost
 
Aurelia Meetup Paris
Aurelia Meetup ParisAurelia Meetup Paris
Aurelia Meetup Paris
Ahmed Radjdi
 

Similar to Node.js server-side rendering (20)

Angular server side rendering - Strategies & Technics
Angular server side rendering - Strategies & Technics Angular server side rendering - Strategies & Technics
Angular server side rendering - Strategies & Technics
 
Reduxing like a pro
Reduxing like a proReduxing like a pro
Reduxing like a pro
 
Express JS
Express JSExpress JS
Express JS
 
Maciej Treder ''Angular Universal - a medicine for the Angular + SEO/CDN issu...
Maciej Treder ''Angular Universal - a medicine for the Angular + SEO/CDN issu...Maciej Treder ''Angular Universal - a medicine for the Angular + SEO/CDN issu...
Maciej Treder ''Angular Universal - a medicine for the Angular + SEO/CDN issu...
 
Building Web Apps with Express
Building Web Apps with ExpressBuilding Web Apps with Express
Building Web Apps with Express
 
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
 
React native by example by Vadim Ruban
React native by example by Vadim RubanReact native by example by Vadim Ruban
React native by example by Vadim Ruban
 
Universal JavaScript Web Applications with React - Luciano Mammino - Codemoti...
Universal JavaScript Web Applications with React - Luciano Mammino - Codemoti...Universal JavaScript Web Applications with React - Luciano Mammino - Codemoti...
Universal JavaScript Web Applications with React - Luciano Mammino - Codemoti...
 
Silex Cheat Sheet
Silex Cheat SheetSilex Cheat Sheet
Silex Cheat Sheet
 
Angular 1.x vs. Angular 2.x
Angular 1.x vs. Angular 2.xAngular 1.x vs. Angular 2.x
Angular 1.x vs. Angular 2.x
 
Node.js: scalability tips - Azure Dev Community Vijayawada
Node.js: scalability tips - Azure Dev Community VijayawadaNode.js: scalability tips - Azure Dev Community Vijayawada
Node.js: scalability tips - Azure Dev Community Vijayawada
 
Advanced redux
Advanced reduxAdvanced redux
Advanced redux
 
Universal JavaScript
Universal JavaScriptUniversal JavaScript
Universal JavaScript
 
Building Universal Web Apps with React ForwardJS 2017
Building Universal Web Apps with React ForwardJS 2017Building Universal Web Apps with React ForwardJS 2017
Building Universal Web Apps with React ForwardJS 2017
 
Restful Web Service
Restful Web ServiceRestful Web Service
Restful Web Service
 
10 Excellent Ways to Secure Spring Boot Applications - Okta Webinar 2020
10 Excellent Ways to Secure Spring Boot Applications - Okta Webinar 202010 Excellent Ways to Secure Spring Boot Applications - Okta Webinar 2020
10 Excellent Ways to Secure Spring Boot Applications - Okta Webinar 2020
 
Maciej Treder "Server-side rendering with Angular—be faster and more SEO, CDN...
Maciej Treder "Server-side rendering with Angular—be faster and more SEO, CDN...Maciej Treder "Server-side rendering with Angular—be faster and more SEO, CDN...
Maciej Treder "Server-side rendering with Angular—be faster and more SEO, CDN...
 
Dart Power Tools
Dart Power ToolsDart Power Tools
Dart Power Tools
 
Building an angular application -1 ( API: Golang, Database: Postgres) v1.0
Building an angular application -1 ( API: Golang, Database: Postgres) v1.0Building an angular application -1 ( API: Golang, Database: Postgres) v1.0
Building an angular application -1 ( API: Golang, Database: Postgres) v1.0
 
Aurelia Meetup Paris
Aurelia Meetup ParisAurelia Meetup Paris
Aurelia Meetup Paris
 

More from The Software House

Jak kraść miliony, czyli o błędach bezpieczeństwa, które mogą spotkać również...
Jak kraść miliony, czyli o błędach bezpieczeństwa, które mogą spotkać również...Jak kraść miliony, czyli o błędach bezpieczeństwa, które mogą spotkać również...
Jak kraść miliony, czyli o błędach bezpieczeństwa, które mogą spotkać również...
The Software House
 
Uszanowanko Podsumowanko
Uszanowanko PodsumowankoUszanowanko Podsumowanko
Uszanowanko Podsumowanko
The Software House
 
Jak efektywnie podejść do certyfikacji w AWS?
Jak efektywnie podejść do certyfikacji w AWS?Jak efektywnie podejść do certyfikacji w AWS?
Jak efektywnie podejść do certyfikacji w AWS?
The Software House
 
O co chodzi z tą dostępnością cyfrową?
O co chodzi z tą dostępnością cyfrową?O co chodzi z tą dostępnością cyfrową?
O co chodzi z tą dostępnością cyfrową?
The Software House
 
Chat tekstowy z użyciem Amazon Chime
Chat tekstowy z użyciem Amazon ChimeChat tekstowy z użyciem Amazon Chime
Chat tekstowy z użyciem Amazon Chime
The Software House
 
Migracje danych serverless
Migracje danych serverlessMigracje danych serverless
Migracje danych serverless
The Software House
 
Jak nie zwariować z architekturą Serverless?
Jak nie zwariować z architekturą Serverless?Jak nie zwariować z architekturą Serverless?
Jak nie zwariować z architekturą Serverless?
The Software House
 
Analiza semantyczna artykułów prasowych w 5 sprintów z użyciem AWS
Analiza semantyczna artykułów prasowych w 5 sprintów z użyciem AWSAnaliza semantyczna artykułów prasowych w 5 sprintów z użyciem AWS
Analiza semantyczna artykułów prasowych w 5 sprintów z użyciem AWS
The Software House
 
Feature flags na ratunek projektu w JavaScript
Feature flags na ratunek projektu w JavaScriptFeature flags na ratunek projektu w JavaScript
Feature flags na ratunek projektu w JavaScript
The Software House
 
Typowanie nominalne w TypeScript
Typowanie nominalne w TypeScriptTypowanie nominalne w TypeScript
Typowanie nominalne w TypeScript
The Software House
 
Automatyzacja tworzenia frontendu z wykorzystaniem GraphQL
Automatyzacja tworzenia frontendu z wykorzystaniem GraphQLAutomatyzacja tworzenia frontendu z wykorzystaniem GraphQL
Automatyzacja tworzenia frontendu z wykorzystaniem GraphQL
The Software House
 
Serverless Compose vs hurtownia danych
Serverless Compose vs hurtownia danychServerless Compose vs hurtownia danych
Serverless Compose vs hurtownia danych
The Software House
 
Testy API: połączenie z bazą danych czy implementacja w pamięci
Testy API: połączenie z bazą danych czy implementacja w pamięciTesty API: połączenie z bazą danych czy implementacja w pamięci
Testy API: połączenie z bazą danych czy implementacja w pamięci
The Software House
 
Jak skutecznie read model. Case study
Jak skutecznie read model. Case studyJak skutecznie read model. Case study
Jak skutecznie read model. Case study
The Software House
 
Firestore czyli ognista baza od giganta z Doliny Krzemowej
Firestore czyli ognista baza od giganta z Doliny KrzemowejFirestore czyli ognista baza od giganta z Doliny Krzemowej
Firestore czyli ognista baza od giganta z Doliny Krzemowej
The Software House
 
Jak utrzymać stado Lambd w ryzach
Jak utrzymać stado Lambd w ryzachJak utrzymać stado Lambd w ryzach
Jak utrzymać stado Lambd w ryzach
The Software House
 
Jak poskromić AWS?
Jak poskromić AWS?Jak poskromić AWS?
Jak poskromić AWS?
The Software House
 
O łączeniu Storyblok i Next.js
O łączeniu Storyblok i Next.jsO łączeniu Storyblok i Next.js
O łączeniu Storyblok i Next.js
The Software House
 
Amazon Step Functions. Sposób na implementację procesów w chmurze
Amazon Step Functions. Sposób na implementację procesów w chmurzeAmazon Step Functions. Sposób na implementację procesów w chmurze
Amazon Step Functions. Sposób na implementację procesów w chmurze
The Software House
 
Od Figmy do gotowej aplikacji bez linijki kodu
Od Figmy do gotowej aplikacji bez linijki koduOd Figmy do gotowej aplikacji bez linijki kodu
Od Figmy do gotowej aplikacji bez linijki kodu
The Software House
 

More from The Software House (20)

Jak kraść miliony, czyli o błędach bezpieczeństwa, które mogą spotkać również...
Jak kraść miliony, czyli o błędach bezpieczeństwa, które mogą spotkać również...Jak kraść miliony, czyli o błędach bezpieczeństwa, które mogą spotkać również...
Jak kraść miliony, czyli o błędach bezpieczeństwa, które mogą spotkać również...
 
Uszanowanko Podsumowanko
Uszanowanko PodsumowankoUszanowanko Podsumowanko
Uszanowanko Podsumowanko
 
Jak efektywnie podejść do certyfikacji w AWS?
Jak efektywnie podejść do certyfikacji w AWS?Jak efektywnie podejść do certyfikacji w AWS?
Jak efektywnie podejść do certyfikacji w AWS?
 
O co chodzi z tą dostępnością cyfrową?
O co chodzi z tą dostępnością cyfrową?O co chodzi z tą dostępnością cyfrową?
O co chodzi z tą dostępnością cyfrową?
 
Chat tekstowy z użyciem Amazon Chime
Chat tekstowy z użyciem Amazon ChimeChat tekstowy z użyciem Amazon Chime
Chat tekstowy z użyciem Amazon Chime
 
Migracje danych serverless
Migracje danych serverlessMigracje danych serverless
Migracje danych serverless
 
Jak nie zwariować z architekturą Serverless?
Jak nie zwariować z architekturą Serverless?Jak nie zwariować z architekturą Serverless?
Jak nie zwariować z architekturą Serverless?
 
Analiza semantyczna artykułów prasowych w 5 sprintów z użyciem AWS
Analiza semantyczna artykułów prasowych w 5 sprintów z użyciem AWSAnaliza semantyczna artykułów prasowych w 5 sprintów z użyciem AWS
Analiza semantyczna artykułów prasowych w 5 sprintów z użyciem AWS
 
Feature flags na ratunek projektu w JavaScript
Feature flags na ratunek projektu w JavaScriptFeature flags na ratunek projektu w JavaScript
Feature flags na ratunek projektu w JavaScript
 
Typowanie nominalne w TypeScript
Typowanie nominalne w TypeScriptTypowanie nominalne w TypeScript
Typowanie nominalne w TypeScript
 
Automatyzacja tworzenia frontendu z wykorzystaniem GraphQL
Automatyzacja tworzenia frontendu z wykorzystaniem GraphQLAutomatyzacja tworzenia frontendu z wykorzystaniem GraphQL
Automatyzacja tworzenia frontendu z wykorzystaniem GraphQL
 
Serverless Compose vs hurtownia danych
Serverless Compose vs hurtownia danychServerless Compose vs hurtownia danych
Serverless Compose vs hurtownia danych
 
Testy API: połączenie z bazą danych czy implementacja w pamięci
Testy API: połączenie z bazą danych czy implementacja w pamięciTesty API: połączenie z bazą danych czy implementacja w pamięci
Testy API: połączenie z bazą danych czy implementacja w pamięci
 
Jak skutecznie read model. Case study
Jak skutecznie read model. Case studyJak skutecznie read model. Case study
Jak skutecznie read model. Case study
 
Firestore czyli ognista baza od giganta z Doliny Krzemowej
Firestore czyli ognista baza od giganta z Doliny KrzemowejFirestore czyli ognista baza od giganta z Doliny Krzemowej
Firestore czyli ognista baza od giganta z Doliny Krzemowej
 
Jak utrzymać stado Lambd w ryzach
Jak utrzymać stado Lambd w ryzachJak utrzymać stado Lambd w ryzach
Jak utrzymać stado Lambd w ryzach
 
Jak poskromić AWS?
Jak poskromić AWS?Jak poskromić AWS?
Jak poskromić AWS?
 
O łączeniu Storyblok i Next.js
O łączeniu Storyblok i Next.jsO łączeniu Storyblok i Next.js
O łączeniu Storyblok i Next.js
 
Amazon Step Functions. Sposób na implementację procesów w chmurze
Amazon Step Functions. Sposób na implementację procesów w chmurzeAmazon Step Functions. Sposób na implementację procesów w chmurze
Amazon Step Functions. Sposób na implementację procesów w chmurze
 
Od Figmy do gotowej aplikacji bez linijki kodu
Od Figmy do gotowej aplikacji bez linijki koduOd Figmy do gotowej aplikacji bez linijki kodu
Od Figmy do gotowej aplikacji bez linijki kodu
 

Recently uploaded

ER(Entity Relationship) Diagram for online shopping - TAE
ER(Entity Relationship) Diagram for online shopping - TAEER(Entity Relationship) Diagram for online shopping - TAE
ER(Entity Relationship) Diagram for online shopping - TAE
Himani415946
 
1比1复刻(bath毕业证书)英国巴斯大学毕业证学位证原版一模一样
1比1复刻(bath毕业证书)英国巴斯大学毕业证学位证原版一模一样1比1复刻(bath毕业证书)英国巴斯大学毕业证学位证原版一模一样
1比1复刻(bath毕业证书)英国巴斯大学毕业证学位证原版一模一样
3ipehhoa
 
Living-in-IT-era-Module-7-Imaging-and-Design-for-Social-Impact.pptx
Living-in-IT-era-Module-7-Imaging-and-Design-for-Social-Impact.pptxLiving-in-IT-era-Module-7-Imaging-and-Design-for-Social-Impact.pptx
Living-in-IT-era-Module-7-Imaging-and-Design-for-Social-Impact.pptx
TristanJasperRamos
 
Output determination SAP S4 HANA SAP SD CC
Output determination SAP S4 HANA SAP SD CCOutput determination SAP S4 HANA SAP SD CC
Output determination SAP S4 HANA SAP SD CC
ShahulHameed54211
 
This 7-second Brain Wave Ritual Attracts Money To You.!
This 7-second Brain Wave Ritual Attracts Money To You.!This 7-second Brain Wave Ritual Attracts Money To You.!
This 7-second Brain Wave Ritual Attracts Money To You.!
nirahealhty
 
Latest trends in computer networking.pptx
Latest trends in computer networking.pptxLatest trends in computer networking.pptx
Latest trends in computer networking.pptx
JungkooksNonexistent
 
How to Use Contact Form 7 Like a Pro.pptx
How to Use Contact Form 7 Like a Pro.pptxHow to Use Contact Form 7 Like a Pro.pptx
How to Use Contact Form 7 Like a Pro.pptx
Gal Baras
 
test test test test testtest test testtest test testtest test testtest test ...
test test  test test testtest test testtest test testtest test testtest test ...test test  test test testtest test testtest test testtest test testtest test ...
test test test test testtest test testtest test testtest test testtest test ...
Arif0071
 
History+of+E-commerce+Development+in+China-www.cfye-commerce.shop
History+of+E-commerce+Development+in+China-www.cfye-commerce.shopHistory+of+E-commerce+Development+in+China-www.cfye-commerce.shop
History+of+E-commerce+Development+in+China-www.cfye-commerce.shop
laozhuseo02
 
1.Wireless Communication System_Wireless communication is a broad term that i...
1.Wireless Communication System_Wireless communication is a broad term that i...1.Wireless Communication System_Wireless communication is a broad term that i...
1.Wireless Communication System_Wireless communication is a broad term that i...
JeyaPerumal1
 
guildmasters guide to ravnica Dungeons & Dragons 5...
guildmasters guide to ravnica Dungeons & Dragons 5...guildmasters guide to ravnica Dungeons & Dragons 5...
guildmasters guide to ravnica Dungeons & Dragons 5...
Rogerio Filho
 
Multi-cluster Kubernetes Networking- Patterns, Projects and Guidelines
Multi-cluster Kubernetes Networking- Patterns, Projects and GuidelinesMulti-cluster Kubernetes Networking- Patterns, Projects and Guidelines
Multi-cluster Kubernetes Networking- Patterns, Projects and Guidelines
Sanjeev Rampal
 
The+Prospects+of+E-Commerce+in+China.pptx
The+Prospects+of+E-Commerce+in+China.pptxThe+Prospects+of+E-Commerce+in+China.pptx
The+Prospects+of+E-Commerce+in+China.pptx
laozhuseo02
 
原版仿制(uob毕业证书)英国伯明翰大学毕业证本科学历证书原版一模一样
原版仿制(uob毕业证书)英国伯明翰大学毕业证本科学历证书原版一模一样原版仿制(uob毕业证书)英国伯明翰大学毕业证本科学历证书原版一模一样
原版仿制(uob毕业证书)英国伯明翰大学毕业证本科学历证书原版一模一样
3ipehhoa
 
BASIC C++ lecture NOTE C++ lecture 3.pptx
BASIC C++ lecture NOTE C++ lecture 3.pptxBASIC C++ lecture NOTE C++ lecture 3.pptx
BASIC C++ lecture NOTE C++ lecture 3.pptx
natyesu
 
急速办(bedfordhire毕业证书)英国贝德福特大学毕业证成绩单原版一模一样
急速办(bedfordhire毕业证书)英国贝德福特大学毕业证成绩单原版一模一样急速办(bedfordhire毕业证书)英国贝德福特大学毕业证成绩单原版一模一样
急速办(bedfordhire毕业证书)英国贝德福特大学毕业证成绩单原版一模一样
3ipehhoa
 

Recently uploaded (16)

ER(Entity Relationship) Diagram for online shopping - TAE
ER(Entity Relationship) Diagram for online shopping - TAEER(Entity Relationship) Diagram for online shopping - TAE
ER(Entity Relationship) Diagram for online shopping - TAE
 
1比1复刻(bath毕业证书)英国巴斯大学毕业证学位证原版一模一样
1比1复刻(bath毕业证书)英国巴斯大学毕业证学位证原版一模一样1比1复刻(bath毕业证书)英国巴斯大学毕业证学位证原版一模一样
1比1复刻(bath毕业证书)英国巴斯大学毕业证学位证原版一模一样
 
Living-in-IT-era-Module-7-Imaging-and-Design-for-Social-Impact.pptx
Living-in-IT-era-Module-7-Imaging-and-Design-for-Social-Impact.pptxLiving-in-IT-era-Module-7-Imaging-and-Design-for-Social-Impact.pptx
Living-in-IT-era-Module-7-Imaging-and-Design-for-Social-Impact.pptx
 
Output determination SAP S4 HANA SAP SD CC
Output determination SAP S4 HANA SAP SD CCOutput determination SAP S4 HANA SAP SD CC
Output determination SAP S4 HANA SAP SD CC
 
This 7-second Brain Wave Ritual Attracts Money To You.!
This 7-second Brain Wave Ritual Attracts Money To You.!This 7-second Brain Wave Ritual Attracts Money To You.!
This 7-second Brain Wave Ritual Attracts Money To You.!
 
Latest trends in computer networking.pptx
Latest trends in computer networking.pptxLatest trends in computer networking.pptx
Latest trends in computer networking.pptx
 
How to Use Contact Form 7 Like a Pro.pptx
How to Use Contact Form 7 Like a Pro.pptxHow to Use Contact Form 7 Like a Pro.pptx
How to Use Contact Form 7 Like a Pro.pptx
 
test test test test testtest test testtest test testtest test testtest test ...
test test  test test testtest test testtest test testtest test testtest test ...test test  test test testtest test testtest test testtest test testtest test ...
test test test test testtest test testtest test testtest test testtest test ...
 
History+of+E-commerce+Development+in+China-www.cfye-commerce.shop
History+of+E-commerce+Development+in+China-www.cfye-commerce.shopHistory+of+E-commerce+Development+in+China-www.cfye-commerce.shop
History+of+E-commerce+Development+in+China-www.cfye-commerce.shop
 
1.Wireless Communication System_Wireless communication is a broad term that i...
1.Wireless Communication System_Wireless communication is a broad term that i...1.Wireless Communication System_Wireless communication is a broad term that i...
1.Wireless Communication System_Wireless communication is a broad term that i...
 
guildmasters guide to ravnica Dungeons & Dragons 5...
guildmasters guide to ravnica Dungeons & Dragons 5...guildmasters guide to ravnica Dungeons & Dragons 5...
guildmasters guide to ravnica Dungeons & Dragons 5...
 
Multi-cluster Kubernetes Networking- Patterns, Projects and Guidelines
Multi-cluster Kubernetes Networking- Patterns, Projects and GuidelinesMulti-cluster Kubernetes Networking- Patterns, Projects and Guidelines
Multi-cluster Kubernetes Networking- Patterns, Projects and Guidelines
 
The+Prospects+of+E-Commerce+in+China.pptx
The+Prospects+of+E-Commerce+in+China.pptxThe+Prospects+of+E-Commerce+in+China.pptx
The+Prospects+of+E-Commerce+in+China.pptx
 
原版仿制(uob毕业证书)英国伯明翰大学毕业证本科学历证书原版一模一样
原版仿制(uob毕业证书)英国伯明翰大学毕业证本科学历证书原版一模一样原版仿制(uob毕业证书)英国伯明翰大学毕业证本科学历证书原版一模一样
原版仿制(uob毕业证书)英国伯明翰大学毕业证本科学历证书原版一模一样
 
BASIC C++ lecture NOTE C++ lecture 3.pptx
BASIC C++ lecture NOTE C++ lecture 3.pptxBASIC C++ lecture NOTE C++ lecture 3.pptx
BASIC C++ lecture NOTE C++ lecture 3.pptx
 
急速办(bedfordhire毕业证书)英国贝德福特大学毕业证成绩单原版一模一样
急速办(bedfordhire毕业证书)英国贝德福特大学毕业证成绩单原版一模一样急速办(bedfordhire毕业证书)英国贝德福特大学毕业证成绩单原版一模一样
急速办(bedfordhire毕业证书)英国贝德福特大学毕业证成绩单原版一模一样
 

Node.js server-side rendering

  • 3. Server-side rendering • Przeglądarka wykonuje zapytanie, serwer zwraca przygotowany HTML
  • 4.
  • 5. Server-side rendering • W kontekście SPA - uruchomienie kodu aplikacji i renderera po stronie serwera (kod uniwersalny)
  • 6.
  • 7.
  • 8. +
  • 9.
  • 10. src/client/index.js import React from 'react'; import ReactDOM from ‘react-dom'; import {BrowserRouter} from 'react-router-dom'; import App from './app/app.component'; ReactDOM.render( <BrowserRouter> <App /> </BrowserRouter>, document.getElementById('root') ); początkowy kod - uruchomienie spa
  • 11. src/server/index.js import express from 'express'; const app = express(); app.use(express.static('dist')); app.get('*', (req, res) => { // doServerSideRenderingPls(); }); app.listen(3000); początkowy kod - serwer
  • 12. Problem 1: Routing • Jak przełożyć routing z przeglądarki na serwer? 🤷 export default class App extends Component { render() { return ( <div className="container"> ... <Route exact path="/" component={Home} /> <Route path="/contact" component={Contact} /> </div> </div> ); } }
  • 13. Problem 1: Routing • Routing jest uniwersalny. export default class App extends Component { render() { return ( <div className="container"> ... <Route exact path="/" component={Home} /> <Route path="/contact" component={Contact} /> </div> </div> ); } }
  • 14. src/server/index.js ... app.get('*', (req, res) => { const context = {}; const appString = renderToString( <StaticRouter location={req.url} context={context}> <App /> </StaticRouter> ); if (context.url) { // redirect was done in client-side routing res.redirect(301, context.url); } else { res.send(` <!DOCTYPE html> . . . <body> <div id="root">${appString}</div> <script type="text/javascript" src="client.js"></script> </body> </html> `); } }); ... server side rendering z routerem
  • 15. src/server/index.js ... app.get('*', (req, res) => { const context = {}; const appString = renderToString( <StaticRouter location={req.url} context={context}> <App /> </StaticRouter> ); if (context.url) { // redirect was done in client-side routing res.redirect(301, context.url); } else { res.send(` <!DOCTYPE html> . . . <body> <div id="root">${appString}</div> <script type="text/javascript" src="client.js"></script> </body> </html> `); } }); ... <div class="container" data-reactroot=""> <div> <div class="navbar"><span class="brand">Taylor Swift</span> <ul> <li><a href="/">Home</a></li> <li><a href="/contact">Contact</a></li> </ul> </div> <hr/> <div> <p> Taylor Alison Swift (born December 13, 1989) is an … </p> <ul></ul> </div> </div> </div> server side rendering z routerem
  • 16. src/server/index.js ... app.get('*', (req, res) => { const context = {}; const appString = renderToString( <StaticRouter location={req.url} context={context}> <App /> </StaticRouter> ); if (context.url) { // redirect was done in client-side routing res.redirect(301, context.url); } else { res.send(` <!DOCTYPE html> . . . <body> <div id="root">${appString}</div> <script type="text/javascript" src="client.js"></script> </body> </html> `); } }); ... server side rendering z routerem <div class="container" data-reactroot=""> <div> <div class="navbar"><span class="brand">Taylor Swift</span> <ul> <li><a href="/">Home</a></li> <li><a href="/contact">Contact</a></li> </ul> </div> <hr/> <div> <p> Taylor Alison Swift (born December 13, 1989) is an … </p> <ul></ul> </div> </div> </div>
  • 17. src/server/index.js ... app.get('*', (req, res) => { const context = {}; const appString = renderToString( <StaticRouter location={req.url} context={context}> <App /> </StaticRouter> ); if (context.url) { // redirect was done in client-side routing res.redirect(301, context.url); } else { res.send(` <!DOCTYPE html> . . . <body> <div id="root">${appString}</div> <script type="text/javascript" src="client.js"></script> </body> </html> `); } }); ... server side rendering z routerem <div class="container" data-reactroot=""> <div> <div class="navbar"><span class="brand">Taylor Swift</span> <ul> <li><a href="/">Home</a></li> <li><a href="/contact">Contact</a></li> </ul> </div> <hr/> <div> <p> Taylor Alison Swift (born December 13, 1989) is an … </p> <ul></ul> </div> </div> </div>
  • 18. Problem 2: Dane • Jak wypełnić dokument danymi podczas SSR? 🌅 async loadData() { const res = await fetch('https://api.imgur.com/3/gallery/r/taylorswift', { headers: {Authorization: ‘Client-ID shAK3-it-0ff’}, }); ... this.setState({pics}); }
  • 19. Problem 2: Dane Server-side render and return HTML Fetch JS app code Run app client-side loadData()
  • 20. Problem 2: Dane Server-side render and return HTML Fetch JS app code Run app client- side loadData()loadData()
  • 21. src/server/index.js ... const getTaytayPics = async () => { const res = await fetch('https://api.imgur.com/3/gallery/r/taylorswift', { headers: {Authorization: ‘Client-ID l00K-wH4t-Y0u-m4DE-m3-D0‘}, }); . . . return pics; }; ... app.get('*', async (req, res) => { const context = {}; if (req.url === '/') { context.pics = await getTaytayPics(); } const appString = renderToString( <StaticRouter location={req.url} context={context}> <App /> </StaticRouter> ); ... server side rendering + pobieranie danych v1
  • 22. src/server/index.js ... const getTaytayPics = async () => { const res = await fetch('https://api.imgur.com/3/gallery/r/taylorswift', { headers: {Authorization: ‘Client-ID l00K-wH4t-Y0u-m4DE-m3-D0‘}, }); . . . return pics; }; ... app.get('*', async (req, res) => { const context = {}; if (req.url === '/') { context.pics = await getTaytayPics(); } const appString = renderToString( <StaticRouter location={req.url} context={context}> <App /> </StaticRouter> ); ... server side rendering + pobieranie danych v1
  • 23. src/client/app/home/home.component.js export class Home extends Component { . . . componentWillMount() { if (this.props.staticContext && this.props.staticContext.pics) { this.setState({pics: this.props.staticContext.pics}) } else { this.loadData(); } } komponent react - wykorzystanie danych podczas ssr
  • 24. Problem 2: Dane Server-side render and return HTML Fetch JS app code Run app client- side loadData()loadData()
  • 25. src/server/index.js app.get('*', async (req, res) => { . . . if (req.url === '/') { context.pics = await getTaytayPics(); } . . . } else { res.send(`<!DOCTYPE html> . . .> <script> window.APP_STATE = ${JSON.stringify({pics: context.pics})}; </script> <script type="text/javascript" src="client.js"></script> </body> </html>`); server side rendering - przekazanie informacji
  • 26. src/client/app/home/home.component.js componentWillMount() { if (this.props.staticContext && this.props.staticContext.pics) { this.setState({pics: this.props.staticContext.pics}); } else if (window && window.APP_STATE && window.APP_STATE.pics) { this.setState({pics: window.APP_STATE.pics}) } else { this.loadData(); } } komponent react - wykorzystanie danych z ssr
  • 27. src/server/index.js ... const getTaytayPics = async () => { const res = await fetch('https://api.imgur.com/3/gallery/r/taylorswift', { headers: {Authorization: ‘Client-ID l00K-wH4t-Y0u-m4DE-m3-D0‘}, }); . . . return pics; }; ... app.get('*', async (req, res) => { const context = {}; if (req.url === '/') { context.pics = await getTaytayPics(); } const appString = renderToString( <StaticRouter location={req.url} context={context}> <App /> </StaticRouter> ); ... DRY?
  • 28. src/client/app/app.routes.js import {Home} from './home/home.component'; import {Contact} from './contact/contact.component'; export const routes = [ { component: Home, path: '/', exact: true, }, { component: Contact, path: '/contact', }, ]; refactoring routingu
  • 29. src/client/app/home/home.component.js import fetch from ‘isomorphic-fetch'; . . . static async loadData() { const res = await fetch('https://api.imgur.com/3/gallery/r/taylorswift', { headers: {Authorization: 'Client-ID w1ld3est-dr3am5‘}, }); return pics; } . . . } refactoring komponentu react
  • 30. src/server/index.js import {StaticRouter, matchPath} from 'react-router'; import {routes} from '../client/app/app.routes'; . . . app.get('*', async (req, res) => { . . . const matchedRoute = routes.find(route => matchPath(req.path, route)); if (matchedRoute) { if (matchedRoute.component && matchedRoute.component.loadData) { context.data = await matchedRoute.component.loadData(); } } else { return res.sendStatus(404); } const appString = renderToString( . . . refactoring pobierania danych na serwerze
  • 31. Problem 2.5: Dane • Jak wypełnić danymi store?
  • 32. src/client/app/redux/taytay/taytay.actions.js import fetch from 'isomorphic-fetch'; export const fetchPics = () => async dispatch => { const res = await fetch('https://api.imgur.com/3/gallery/r/ taylorswift', { headers: {Authorization: 'Client-ID 0447601918a7bb5'}, }); . . . return dispatch(setPics(pics)); }; export const setPics = pics => ({. . .}); przeniesienie pobierania danych do akcji
  • 33. src/client/app/home/home.component.js import {fetchPics} from '../redux/taytay/taytay.actions'; export class Home extends Component { static loadData = store => { return store.dispatch(fetchPics()); }; . . . wykorzystanie akcji w komponencie
  • 34. src/client/index.js import {createAppStore} from './create-store'; const store = createAppStore(window.APP_STATE || {}); ReactDOM.hydrate( <Provider store={store}> <BrowserRouter> <App /> </BrowserRouter> </Provider>, document.getElementById('root') ); stworzenie store + odtworzenie stanu z ssr
  • 35. src/server/index.js import {createAppStore} from '../client/create-store'; . . . app.get('*', async (req, res) => { const store = createAppStore({}); const matchedRoute = routes.find(route => matchPath(req.path, route)); if (matchedRoute) { if (matchedRoute.component && matchedRoute.component.loadData) { await matchedRoute.component.loadData(store); } . . . } else { const state = store.getState(); const html = `<!DOCTYPE html> . . . <div id="root">${appString}</div> <script> window.APP_STATE = ${JSON.stringify(state)}; </script> . . . } }); stworzenie store + inicjalizacja stanu podczas ssr
  • 36. src/server/index.js import {createAppStore} from '../client/create-store'; . . . app.get('*', async (req, res) => { const store = createAppStore({}); const matchedRoute = routes.find(route => matchPath(req.path, route)); if (matchedRoute) { if (matchedRoute.component && matchedRoute.component.loadData) { await matchedRoute.component.loadData(store); } . . . } else { const state = store.getState(); const html = `<!DOCTYPE html> . . . <div id="root">${appString}</div> <script> window.APP_STATE = ${JSON.stringify(state)}; </script> . . . } }); stworzenie store + inicjalizacja stanu podczas ssr
  • 37. src/server/index.js import {createAppStore} from '../client/create-store'; . . . app.get('*', async (req, res) => { const store = createAppStore({}); const matchedRoute = routes.find(route => matchPath(req.path, route)); if (matchedRoute) { if (matchedRoute.component && matchedRoute.component.loadData) { await matchedRoute.component.loadData(store); } . . . } else { const state = store.getState(); const html = `<!DOCTYPE html> . . . <div id="root">${appString}</div> <script> window.APP_STATE = ${JSON.stringify(state)}; </script> . . . } }); stworzenie store + inicjalizacja stanu podczas ssr
  • 38. Problem 3: Wydajność • Jak szybko będzie działał serwer?
  • 39. Problem 3: Wydajność • Jak szybko będzie działał serwer?
  • 40. src/server/index.js import cache from 'memory-cache'; . . . app.use('*', (req, res, next) => { const cachedHtml = cache.get(req.originalUrl); if (cachedHtml) { res.send(cachedHtml); } else { next(); } }); app.get('*', async (req, res) => { . . . cache.put(req.path, html); res.send(html); } }); dodanie cache
  • 42. src/server/index.js import {createCacheStream} from ‘./cache-stream'; app.get('*', async (req, res) => { . . . const cacheStream = createCacheStream(req.path, cache); cacheStream.pipe(res); . . . cacheStream.write(`<!DOCTYPE html> <html> . . . <body> <div id="root">`); const appStream = renderToNodeStream( . . . ); appStream.pipe(cacheStream, {end: false}); appStream.on('end', () => { cacheStream.end(` . . . </body> </html>`); }); } }); wykorzystanie renderToNodeStream
  • 43. src/server/cache-stream.js import {Transform} from 'stream'; export const createCacheStream = (key, cache) => { const bufferedChunks = []; return new Transform({ transform(data, enc, cb) { bufferedChunks.push(data); cb(null, data); }, flush(cb) { cache.put(key, Buffer.concat(bufferedChunks).toString()); cb(); }, }); }; stream pomocniczy - cache
  • 44. Problem 4: Uwierzytelnianie • Jak renderować zawartość wymagającą autoryzacji? 🔐 Fetch token from the server Save token in persistent storage Pass token to the server on requests Authenticate & authorize server-side
  • 45. Problem 4: Uwierzytelnianie app.post('/api/login', (req, res) => { res.json({ token: '... ready for it?', }); });
  • 46. src/client/app/redux/auth/auth.actions.js import fetch from 'isomorphic-fetch'; export const login = () => async dispatch => { const res = await fetch(API_URL + '/api/login', { method: 'POST', }); const auth = await res.json(); localStorage.setItem('taytayAuth', auth.token); return dispatch(setToken(auth.token)); }; akcja logowania
  • 47. src/client/app/redux/auth/auth.actions.js import fetch from 'isomorphic-fetch'; export const login = () => async dispatch => { const res = await fetch(API_URL + '/api/login', { method: 'POST', }); const auth = await res.json(); localStorage.setItem('taytayAuth', auth.token); return dispatch(setToken(auth.token)); }; akcja logowania
  • 48. src/client/index.js . . . const token = localStorage.getItem('taytayAuth'); const store = createAppStore({ ...(window.APP_STATE || {}), auth: { token, }, }); ReactDOM.hydrate( . . . ); inicjalizacja store tokenem
  • 49. src/client/app/redux/auth/auth.actions.js const withAuthHoc = WrappedComponent => { return class extends Component { render() { return this.props.isAuth ? ( <WrappedComponent {...this.props} /> ) : ( <Redirect to={{ pathname: '/login', }} /> ); } }; }; prosty guard na route
  • 50. Problem 4: Uwierzytelnianie • Jak renderować zawartość wymagającą autoryzacji? 🔐 🍪
  • 51. src/client/app/redux/auth/auth.actions.js import fetch from 'isomorphic-fetch'; export const login = () => async dispatch => { const res = await fetch(API_URL + '/api/login', { method: 'POST', }); const auth = await res.json(); Cookies.set('taytayAuth', auth.token, {expires: 7, path: '/'}); return dispatch(setToken(auth.token)); }; zamiana localStorage na cookies
  • 52. src/client/index.js . . . const token = Cookies.get('taytayAuth'); const store = createAppStore({ ...(window.APP_STATE || {}), auth: { token, }, }); ReactDOM.hydrate( . . . ); zamiana localStorage na cookies
  • 53. src/server/index.js import cookieParser from 'cookie-parser'; . . . app.use(cookieParser()); . . . app.get('*', async (req, res) => { const token = req.cookies.taytayAuth; const store = createAppStore({ auth: { token, }, }); if (matchedRoute) { if (matchedRoute.private && !token) { return res.redirect(301, ‘/login'); . . . obsługa cookie na serwerze + inicjalizacja store
  • 54. src/server/index.js import cookieParser from 'cookie-parser'; . . . app.use(cookieParser()); . . . app.get('*', async (req, res) => { const token = req.cookies.taytayAuth; const store = createAppStore({ auth: { token, }, }); if (matchedRoute) { if (matchedRoute.private && !token) { return res.redirect(301, ‘/login'); . . . obsługa cookie na serwerze + inicjalizacja store
  • 55. Podsumowując ✅ Komunikacja z API ✅ Integracja z systemem zarządzania stanem ✅ Cache & streaming ✅ Uwierzytelnianie