A new way of client-server conversation
Andrey Sitnik, Evil Martians
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/2583932/Iv5oal8.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3325758/logux-text.png)
From Russia with love
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/4218866/space1.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/4218867/kapsula_big.jpg)
The creator of
Autoprefixer
PostCSS
Browserslist
Slides will be in Twitter
Part 1. The problem.
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/4224153/1945-life-36-hour-war-2.jpg)
Image: Invasion Base on the Moon (Apr, 1948)
The 36-Hour War: Life Magazine, 1945
My family lives in China
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/4221675/mmexport1507916745041.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/4221711/Flag_of_China.png)
Chinese Internet
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3580974/NjFlSJnLzC8.jpg)
40% packet loss
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/4221711/Flag_of_China.png)
Websites on Chinese Internet
Waiting for the next page
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/4219187/512x512.png3b853521-e78a-43a5-b4e3-aac827518645Large.jpg)
No stable Internet
in New York subway
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/4219222/2015-07-23-mr-robot.jpg)
Image: Mr. Robot
No stable Internet
on dotConfs in Paris
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/4219260/2.jpg)
Image: Nicolas Ravelli
Internet is always unstable
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/4219287/1398243193465_01_John_Stanmeyer.jpg)
Image: John Stanmeyer
Long offline
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/4219312/_______.jpg)
Everyday offline
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/4219187/512x512.png3b853521-e78a-43a5-b4e3-aac827518645Large.jpg)
Long ping
Unstable Internet
vs.
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3575227/mPV8Lo8.png)
Image: Nikita Prokopov
Connection in development
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3575245/P0Y2RR6.png)
Image: Nikita Prokopov
Real connection
Common problem #1
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/4219187/512x512.png3b853521-e78a-43a5-b4e3-aac827518645Large.jpg)
Infinite loader
on AJAX errors
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/4221806/ea7569c6-11b1-481e-83f3-0d251d9f7db0.jpg)
Universal solution
Have you tried to press “Reload” button?
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/4221817/reload.png)
Common problem #2
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/4219187/512x512.png3b853521-e78a-43a5-b4e3-aac827518645Large.jpg)
Continue to work
Save
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/4221837/bouncer1.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/4221839/bouncer2.jpg)
Wait
Wait
Request statistics
Success
Failure
Common problem #3
Images: Nikita Prokopov
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3577306/client1.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3577308/client2.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3577310/server.png)
name: A price: A
name: A price: B
name: B price: A
name: B price: A
You always have at least 2 users
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/4222344/responsive-graphic.png)
1
2
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/4222318/Spacecolony3edit.jpeg)
Part 2. The dream.
Image: O'Neill cylinder
All regular web apps…
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3318614/posts___amplifr_2016-12-08_14-51-51.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/4222364/Screenshot_2015-07-19_15.17.01.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/4222369/screen-shot.png)
… will have real-time updates …
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3577306/client1.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3577308/client2.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3577310/server.png)
new comment
update comments list
… will sync changes in background …
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3299063/optimistic-ui2-large-opt.png)
Image author: Denys Mishunov
… will have offline support …
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/4222433/no_twispruce_yes_twisentry_drake_meme_by_imtailsthefoxfan-dajwgjr.jpg)
Read-only offline
Edit in offline
… will not need a lot of code …
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/4242433/t216mGF.png)
… and will use existing ecosystems
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/4222736/b5c.jpg)
Replace DB
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/4224195/The_Command_Service_Module_and_Lunar_Module_orientation_before_contact.png)
Part 3. The analysis.
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/4224126/1-46.jpg)
Complexity curve with AJAX
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3590614/1-qqrhwnLRxCq0a34vTSVLQQ.png)
Complexity
Features
Replace AJAX
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/4224706/ajax_vs_deadpool.jpg)
Image: Deadpool movie
Current approach
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3577308/client2.png)
AJAX request
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/4224739/ancient-maps-with-sea-monsters-2.jpg)
The things
we care about
Don’t care
Distributed systems
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3577306/client1.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3577308/client2.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3577310/server.png)
Sync
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3617217/math.jpg)
Distributed computing is not easy
Image: J. R. Wharton Eyerman
Frameworks reduce complexity
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3298561/logo.png)
Framework is the idea
Ruby on Rails: Getting Real
React: retained rendering of components
Redux: newState = reducer(state, action)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/4222810/430583main_mccall01_full.jpg)
Image: Robert McCall
Part 4. The idea.
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3577239/DHaVv.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3577259/andrey-sitnik.jpg)
Saint Petersburg has best bars in Russia
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/4224782/8r1uwJmWI6xkYOZ3P2rdPA-wide.jpg)
Conflict
Images: Nikita Prokopov
{
title: '伏特加',
likes: 50
}
{
title: '白兰地',
likes: 51
}
?
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3577306/client1.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3577308/client2.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3577310/server.png)
Solution
“Bad programmers worry about the code.
Good programmers worry about
data structures”
— Linus Torvalds
Source: https://lwn.net/Articles/193245/
Event sourcing
Images: Nikita Prokopov
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3577306/client1.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3577308/client2.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3577310/server.png)
['post:title', '伏特加']
['likes:set', 51]
['likes:remove', 1]
['post:title', '伏特加']
['likes:set', 51]
['post:title', '白兰地']
CRDT , 2009
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3295643/RdoCb5i.png)
Conflict-free Replicated Data Types
- Op-based counter
- G-Counter
- PN-Counter
- Non-negative Counter
- LWW-Register
- MV-Register
- G-Set
- 2P-Set
- LWW-element-Set
- PN-Set
- OR-Set
- Add-only monotonic DAG
- Add-remove Partial Order
- Replicated Growable Array
- Continuous sequence
CRDT in JS
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3295699/site-logo.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3295701/ade720cb7a20a6edb4d694fa43ac3a77.jpg)
Swarm.js
@gritzko
Logs everywhere
Redux actions
GraphQL mutations
CRDT
log
Undo/redo
log
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/4232811/k15278544.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3580904/giphy.gif)
Single log to rule them all
Redux
Sync with back-end
CRDT
Undo/redo
Log
Actions log on client
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3577308/client2.png)
{
type: 'CHANGE_NAME',
user: 13,
name: 'New name'
}
Images: Nikita Prokopov
Actions log on server
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3577310/server.png)
Images: Nikita Prokopov
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3580948/5818635fa29df1581f442db2.jpg)
Send new actions to server
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3577308/client2.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3577310/server.png)
WebSocket
Images: Nikita Prokopov
Send new actions after going offline
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3577308/client2.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3577310/server.png)
1
2
3
4
5
— Give me new actions since “4”
Images: Nikita Prokopov
Server could create actions too
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3577310/server.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3577308/client2.png)
Real-time update out of box
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3577308/client2.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3577310/server.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3577306/client1.png)
Cross-tab sync too
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3582060/tabs.png)
Put action to the log
and forget about it
AJAX shows loader
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3577308/client2.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3577310/server.png)
Rename to New
Name: New
Save to DB
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/4219187/512x512.png3b853521-e78a-43a5-b4e3-aac827518645Large.jpg)
Optimistic UI out of box
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3577308/client2.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3577310/server.png)
Rename to New
Name: New
Save to DB
Common UI to show sync status
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3321815/screen_shot_2016-12-09_at_09.55.50_1024.png)
Any client action can be undone on error
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3577308/client2.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3577310/server.png)
Name: New
Rename to New
Name: Old
Error
Undo rename
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/4222790/1-TV6vEgojNX8QFufqzwozeA.jpeg)
Part 5. Details.
Image: Davis Paul Meltzer
How to write open source
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3617202/how-to-draw-an-owl.jpg)
Step 1 Create an idea
Step2 Implement it
Problem 1: order of actions
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3577308/client2.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3577306/client1.png)
A
A
B
B
State: B
State: A
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3580991/t6LRJBB.png)
Images: Nikita Prokopov
Sort by time?
[
[action, { time: Date.now() }]
].sortBy(i => i[1].time)
Distributed time in complicated 1
const time1 = Date.now()
const time2 = Date.now()
time2 !== time1
Distributed time in complicated 2
const time1 = Date.now()
const time2 = Date.now()
time2 >= time1
Distributed time in complicated 3
const client = await client.askTime()
const server = Date.now()
diff(client, server) < HOUR
Vector clock?
const prevTime = 0
time: [
prevTime++,
nodeId
]
Vector clock problems
1. Doesn’t work good in offline
2. Doesn’t connected to actual time (no month, weekday)
Logux time
id: [1489328408677, '10:bfj58s0w', 0]
id: [1489328408677, '10:bfj58s0w', 1]
id: [1489328408678, '10:bfj58s0w', 0]
Milliseconds
Unique Node ID
User ID
Random string
Increase on same millisecond
Server sends current time
[
'connected',
[0, 0], // Protocol version
'server:h4vjdl', // Server unique ID
[
1475316481050, // "connect" message was received
1475316482879 // "connected" message was sent
]
]
Client calculates and applies time difference
{ // Received
id,
time: id[0] + timeFix
}
{ // Sent
id,
time: id[0] - timeFix
}
const roundTrip = now - startTime - authTime
const timeFix = startTime -
receivedTime +
roundTrip / 2
Logux time benefits
- ID is unique for every node
- Сonstantly increasing
- Works offline
- Return month, weekday, etc
- In current node timezone
Problem 2: tabs conflict
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3577310/server.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3582060/tabs.png)
Images author: Sebastien Gabriel & Nikita Prokopov
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3582072/m4xpTE7.png)
Duplicate
Action
Saved
I hate programming
I hate programming
I hate programming
It works!
I love programming
Only the leader tab keeps WS
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3577310/server.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3582060/tabs.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3582072/m4xpTE7.png)
leader
follower
Images author: Sebastien Gabriel & Nikita Prokopov
Synchronized state between tabs
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3582060/tabs.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3582125/Cg36ZhG.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3582125/Cg36ZhG.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/4222812/Soyuz_Spacecraft_in_Orbit_-_GPN-2002-000155.jpg)
Part 6. The reality.
Image: NASA
API
“Talk is cheap. Show me the code.”
— Linus Torvalds
Source: lkml.org/lkml/2000/8/25/132
We care about client JS size
logux + redux ≈
12.6 KB
Thanks to
Size Limit
Redux-compatible API
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/4238647/F2GLaS6.png)
3 ways to dispatch
-
dispatch
-
dispatch.crossTab
-
dispatch.sync
Cross-tab actions
dispatch.crossTab({
type: 'READ_NOTIFICATION',
id: notification.id
})
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3582060/tabs.png)
Sync Redux actions with server
dispatch.sync({
type: 'CHANGE_NAME',
userId: user.id,
name: newName
})
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3577310/server.png)
Instead of AJAX, sagas, etc
showLoader()
fetch('/profile', {
credentials: 'include',
method: 'POST',
form
}).then(r => {
hideLoadaer()
}).catch(e => {
…
})
dispatch.sync({
type: 'CHANGE_NAME',
userId: user.id,
name: newName
})
Node.js framework for server
import { Server } from 'logux-server'
const app = new Server(
subprotocol: '1.0.0',
supports: '1.x',
root: __dirname
})
Handling actions on server
app.type('CHANGE_NAME', {
access(action, meta, userId) {
return userId === action.user
}
process(action, meta) {
…
}
})
Use any DB or send any requests
app.type('CHANGE_NAME', {
…
process (action, meta) {
return db.user.id(action.user).then(u => {
if (isFirstOlder(u.lastChange, meta)) {
return u.changeName(action.name)
}
})
}
Wrap legacy back-end (PHP, Ruby)
app.type('CHANGE_NAME', {
…
process(action) {
return send('POST', 'user/rename.php', {
name: action.name,
id: action.user
})
}
Send actions to client at any time
app.log.add({
type: 'CHANGE_NAME',
name: 'Looser',
user: user.id
})
Server could undo any action on clients
{
type: 'logux/undo',
id: [1489328408677, '10:bfj58s0w', 0],
reason: 'error'
}
Decorator for subscribing
const subscribe = require('react-logux/subscribe')
@subscribe(props => `users/${ props.id }`)
class User extends React.Component {
…
}
Subscription on the server
app.channel('user/:id', (params, action, meta, creator) => {
if (hasAccess(creator.userId, params.id)) return false
return getUser(params.id).then(user => {
app.log.add(userStateAction(user), {
nodeIds: [creator.nodeId]
})
})
})
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/4222814/0_192cb7_bab5bfb7_orig.jpg)
Part 7. Trade-offs.
Image: NASA
VODKA
What Logux is not
Not a CRDT
Not a DB
Framework to implement both
Problem 1: Log cleaning
store.log.on('preadd', (action, meta) => {
if (action.type === 'READ_NOTIFICATION') {
meta.reasons.push('notification:' + action.notificationId)
}
})
onNotificationRemove (id) {
store.log.removeReason('notification:' + id)
}
Problem 2: Complexity
Complexity
App
Net
Logux
Cleaning
App
Right case for Logux
Complexity
App
Net
Logux
Cleaning
App
Problem 3: not for large production sites
Version: 0.2
Amplifr already uses it in production
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3299632/amplifr.jpg)
Brave managers of Amplifr
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/4240521/1498934_814353408637533_6282206497771764653_o.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/4240522/19399743_1445346262226778_2312106252593765231_n.jpg)
30+ contributors
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3585003/3617409.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3584970/11071.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3585014/13414205.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3584991/29658.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3584993/63065.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3584996/95453.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3584999/141334.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3585001/191113.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3584985/19343.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3585016/775692.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3585019/1193817.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3585020/1282980.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3585021/1452165.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3585023/1506905.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3585024/1592680.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3585025/2734841.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3585027/2822264.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3585028/3505878.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3585030/3762784.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3585031/3992528.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3585032/4654180.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3585034/4983850.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3585035/6660939.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3585037/20017097.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3584960/745.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3590621/4834604.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3599604/9166883.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3614830/1516722.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3635886/16529522.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/3635888/22623917.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/467124/images/4235335/557331main_iss027e036710_full.jpg)
Image: Paolo Nespoli
Part 8. The result.
What is Logux?
Replaces AJAX and RESTful
Synchronizes logs (Redux actions)
between client and server
Where to use Logux
Any webapp
where users edit data a lot
Why Logux?
- No need for loaders, sagas for AJAX
- Faster UI with Optimistic UI
- Live updates & Offline-first
- Redux API and any DB
Questions?