Better JSON through streams
Difficulties
- Slow server response
- Coupled responses
- Big AJAX slow and brittle
- Multiple AJAX = complexity
Unix streams
- New-line separated text
- Work with single data points
- Collection of data over time
Three streaming interfaces
- Requesting data
- Responding with data
- Transfering within application
Basic data endpoint
/* server */
router.get('/data-fs-async', function(req, res) {
var catPath = 'filePath';
fs.readFile(catPath, function(err, data){
res.send(data);
});
});
curl -s localhost:3000/data-fs-async | jq . | less
Ask for the data
/* client */
oboe('http://localhost:3000/data-fs-async')
Parse out each point
/* client */
oboe('http://localhost:3000/data-fs-async')
.node('{x y color}', function(point) {
// code using point
})
Use each point
/* client */
oboe('http://localhost:3000/data-fs-async')
.node('{x y color}', function(point) {
var grid = document.querySelector('.grid');
var cell = getCell(grid, point.x, point.y);
cell.classList.add(point.color);
});
Sending a streamed response
Basic data endpoint
/* server */
router.get('/data-basic', function(req, res) {
var catPath = 'filePath';
fs.readFile(catPath, function(err, data){
res.send(data);
});
});
Read as stream instead
/* server */
router.get('/data-fs-read-stream', function(req, res) {
var dataStream = fs.createReadStream('filePath');
dataStream.pipe(res);
});
Two data sources
File on the server
File over the network
Get both as streams
function getFullStream() {
var catPath = path.resolve(__dirname, './cat-points.json');
var catSource = fs.createReadStream(catPath);
var catStream = getPointStream(catSource);
var sunUrl = 'https://raw.githubusercontent.com/JuanCaicedo/better-json-through-streams/master/data/sun-points.json';
var sunSource = request(sunUrl);
var sunStream = getPointStream(sunSource);
return highland([
catStream,
sunStream
])
.merge();
}
Convert strings to points
function getPointStream(sourceStream) {
return highland(function(push, next) {
sourceStream.on('error', function(err) {
push(null, highland.nil);
});
oboe(sourceStream)
.node('{x y color}', function(point) {
push(null, point);
})
.done(function() {
push(null, highland.nil);
});
});
}
Organize and send
router.get('/full-data', function(req, res) {
var pointStream = points.getFullStream()
.map(JSON.stringify)
.intersperse(',');
highland([
'['
pointStream,
']'
])
.invoke('split', [''])
.sequence()
.pipe(res);
});
Better JSON through streams