Expressive HTTP middleware for nodejs and the browser. Rill brings cascading middleware to the browser and enables fully universal web applications. It makes apps enjoyable to write with a simpler top down mental model of your app and free progressive enhancement.
Rill provides the minimum for abstractions over nodejs and the browser enabling things like routing (with redirecting, refreshes and more), cookies, and middleware with the same api.
npm install rill
bower install rill
<script type="text/javascript" src="rill.js"></script>
<script>
define(['rill'], function (rill) {...}) // AMD
window.rill // Global rill if no module system in place.
</script>
All modern browsers are supported including IE10+. IE9 is also supported with a History API polyfill.
Older browsers will also need to polyfill the Promise API, checkout es6-promise for a good polyfill, babel-polyfill also covers this.
- Universal web application framework - Interview with Dylan Piercey
- Isomorphic Javascript, let’s make it easier.
- How to make universal JavaScript applications — Part 1
- Browsers, Servers, and APIs
- Why Everyone is Talking About Isomorphic
- Isomorphic JavaScript: The Future of Web Apps
Rill is the answer to a simple question; Can I run my Express style router in the browser? Turns out you can and it works awesome.
It brings a common interface to many typical app like features in both the browser and nodejs. Many isomorphic frameworks have crazy abstractions and learning curves but with Rill, if you understand Express or Koa, you already know how the routing works! In Rill you get to program much of your application logic using the same api (client or server) including routing, rendering, data fetching and more are easily shared.
Rill also works perfectly as a stand alone nodejs server or a stand alone browser framework. This allows for easy progressive enhancement. If all is well the browser can handle much of your application logic and if JavaScript fails for any reason your server knows exactly what to do.
If you look at the source for Rill here you will quickly notice there is ZERO browser specific code. This is all thanks to @rill/http which is node's HTTP.createServer ported to the browser.
In the browser it works by listening for internal link clicks, form submissions and browser history changes. It will then create a Rill Context for each of these events and emit it through the router, similar to how receiving a request works in nodejs.
It supports everything you'd expect from a client side nodejs server. This includes redirects, refreshes, cookies, scrolling and url updates using the History API.
/**
* The following code can run 100% in the browser or in nodejs.
* Examples use es2015/2016 with Babel and JSX but this is optional.
*/
import rill from 'rill'
const app = rill()
// Universal form data parsing middleware.
import bodyParser from '@rill/body'
app.use(bodyParser())
// Universal react rendering middleware.
import reactRenderer from '@rill/react'
app.use(reactRenderer())
// Example Logger
app.use(async ({ req }, next)=> {
const start = new Date
// Rill uses promises for control flow.
// ES2016 async functions work great as well!
await next()
const ms = new Date - start
console.log(`${req.method} ${req.url} - ${ms}`)
})
// Respond to a GET request.
app.get('/todos', async ({ res })=> {
// Fetch a todolist from some service.
const todolist = await MyTodoListService.getAllTodos()
// Directly set React virtual dom to the body thanks to @rill/react.
// (Checkout @rill/html for universal html diffing).
res.body = (
<html>
<head>
<title>My App</title>
<meta name="description" content="Rill Application">
</head>
<body>
<form action="/add-todo" method="POST">
<h1>Just a plain old form</h1>
<input type="text" name="todo"/>
<button type="submit">Add Todo</button>
</form>
{todolist.length
? todolist.map(renderTodo)
: 'No todos to display.'
}
<script src="/app.js"/>
</body>
</html>
)
})
// Respond to a POST request.
app.post('/add-todo', async ({ req, res })=> {
// We handle form submissions with Rill the same way one would with any other node framework.
// Here we are simply adding the todo via some service.
await MyTodoListService.addTodo({ text: req.body.todo })
// And then we redirect back (same as res.redirect('/todos'))
res.redirect('back')
})
// Start a regular http server.
// In the browser any form submissions or link clicks will intercepted by @rill/http.
app.listen({ port: 80 })
- isbrowser - A browserify transform to remove server-side code.
- isomorphic-fetch - Universal http requests using WHATWG fetch.
- isomorphic-form-data - Send multipart form data universally (able to send files and works with fetch).
- scroll-behavior - @rill/http will automatically try to use the "smooth" scroll-behavior when scrolling to targets on link clicks. This will polyfill that across modern browsers.
- submit-form - Manually trigger Rill navigation in the browser.
- koa-client - Koa clone that runs in the browser, inspired this package.
- monorouter - Another isomorphic router that partially inspired this package.
- Use
npm test
to run tests.