Quiver.js

Rethinking Web Framework

FOSSASIA March 2015

Soares Chen

@soareschen

  • Node / io.js
  • SingaporeJS

How we develop

web apps today

  • Steps involved to render a blog post
GET /blog/first-post

Database

  1. Get database instance
  2. Do SQL query
  3. Result in json

HTML Rendering

  1. Blog post details as json
  2. Content markdown to HTML
  3. Template to HTML

Permission Control

  1. Do authentication
  2. Get database
  3. Query user info
  4. Check permission

Caching

  • Cache blog post info
  • Cache final HTML
  • Cache user info

Many repetitive steps

  • Need to get database everywhere
  • Need to cache everywhere
  • Need user info everywhere

Current Solutions

  • Gobal variables
  • MVC Frameworks
  • Monolithic applications
  • Can we do it better?

The Quiver Way

  • Break down steps into reusable building blocks
  • Each step as isolated functions
  • No globals
  • Functions packaged as components
  • Combine components to form applications

Foundation Architecture

Stream

Handler

Builder

Filter

Middleware

Component

ES6

  • io.js only
  • Traceur / Babel
  • Make full use of ES6 features
    • Promises
    • Symbols
    • Generators

Some Code

import { simpleHandlerBuilder } from 'quiver-core/component'

export let getBlogPostHandler = simpleHandlerBuilder(
  (config) => {
    let { db } = config

    return async function(args) {
      let { postId } = args

      let post = await db.query(...)
      return post
    }
  })
.inputType('void')
.outputType('text')

How about other steps?

let blogPostHandler = simpleHandlerBuilder(...)
  .middleware(databaseMiddleware)
  .middleware(cacheFilter)
  .middleware(permissionFilter)

All Components Together

let databaseMiddleware = configMiddleware(...)
let cacheFilter = streamFilter(...)

let userInfoHandler = simpleHandlerBuilder(...)
  .middleware(databaseMiddleware)
  .middeware(cacheFilter)

let permissionFilter = argsFilter(...)
  .inputHandler(userInfoHandler)

let blogPostHandler = simpleHandlerBuilder(...)
  .middleware(databaseMiddleware)
  .middleware(cacheFilter)
  .middleware(permissionFilter)

Run As Server

import { router } from 'quiver-core/component'
import { startServer } from 'quiver-core/http'
import { 
  userInfoHandler, blogPostHandler, httpFilter 
} from './my-components'

let main = router()
  .paramRoute('/user/:userId', userInfoHandler)
  .paramRoute('/blog/:postId', blogPostHandler)
  .middleware(httpFilter)

startServer(main, config)
.then(server => {
  console.log('Server running...')
})
.catch(err => {
  console.log('error starting server:', err.stack)
})

Component Graph Visualized

Learn More

Thank You!

Feedback?

tweet to @soareschen

Made with Slides.com