Skip to content

Instantly share code, notes, and snippets.

@cmmartin
Last active August 29, 2015 14:27
Show Gist options
  • Save cmmartin/82b32308358b13ecf483 to your computer and use it in GitHub Desktop.
Save cmmartin/82b32308358b13ecf483 to your computer and use it in GitHub Desktop.
Streaming server middleware
import koa from 'koa'
import router from 'koa-route'
import streamView from './stream-view'
const app = koa()
const toJS = str => JSON.parse(str)
const toJSON = js => JSON.stringify(js)
// stream an html page with asynchronous data
app.use(router.get('/html/:title', streamView('html', function* (title) {
yield `
<!DOCTYPE html>
<html>
<head>
<title>${ title }</title>
<meta charset="utf-8">
<link rel="stylesheet" href="/bundle.css?${this.request.querystring}" />
</head>
<body>
<div class="mount-node"></div>
<script type="text/javascript">
window.props = `; yield getProps(this.request.query); yield `;
</script>
<script type="text/javascript" src="bundle.js"></script>
</body>
</html>
`
})))
// stream a json file
// potentially useful for condensing many requests into one
app.use(router.get('/all-the-things.json', streamView('json', function* (title) {
yield getProps(this.request.query)
})))
function *getProps(queryParams) {
return toJSON({
foo: yield fetch(`/some/api`).then(toJS),
bar: yield fetch(`/some/api/${queryParams.thing}`).then(toJS),
baz: yield fetch(`/some/api/${queryParams.anotherThing}`).then(toJS)
})
}
app.listen(3000)
import StreamView from './stream-view'
export default function getMiddleware(type, chunkGenerator) {
return function *(options) {
this.type = type
this.body = new StreamView(this, chunkGenerator.bind(this), options)
}
}
import { Readable } from 'stream'
import co from 'koa/node_modules/co'
export default StreamView
// TODO: allow the chunkGenerator to yield objects as well as strings (JSON.stringify them)
class StreamView extends Readable {
constructor(context, chunkGenerator, options) {
super({})
this.chunkGenerator = chunkGenerator
co.call(this, this.render.bind(this, options)).catch(context.onerror)
}
*render(options) {
for (let chunk of this.chunkGenerator.call(this, options)) {
this.push(typeof chunk === 'string' ? chunk : yield* chunk)
}
this.push(null) // end the stream
}
_read() {} // override
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment