Berliner.

The lightweight web framework

Do you like Sinatra, but find it a little heavy? Bloated with things like simple error handling and CSRF protection; sick of the impedence mismatch of using one language on the server and another on the client; it’s not even asynchronous?!

Berliner is the framework for you! In just 16 lines of delicious CoffeeScript, it gives you all this, using the infinitely scalable horse power of Node.js:

Route requests based on method and pathname

Respond to GET , POST , PUT , DELETE , PATCH , HEAD , and OPTIONS

, , , , , , and Handle WebSocket and EventSource connections

Path-based pattern matching with :named and * matchers

and matchers Construct params from path data, query strings and entity bodies

Read and create cookies

Self-contained encrypted session cookies

Set status code and headers

Perform redirects

Render EJS, HAML and raw text

Serve static files

Define helper functions for request handlers and templates

Works great with MongoDB

Install it

$ npm install berliner

Hello, world

app = require 'berliner' app.get '/', -> 'Hello, world!' app.run 4567

Settings

app.public = __dirname + '/public' # where to find static files app.views = __dirname + '/views' # where to find view templates app.session_secret = 'abcde12345' # key for encrypting sessions

Handling HTTP methods, statuses, headers, etc.

app.get '/confs/:name', -> JSON.stringify @params app.put '/confs/:name', (name) -> name app.get '/download/*.*', -> @params.splat.join ', ' app.post '/confs', -> @status 201 @headers 'Content-Type': 'application/json' JSON.stringify @params app.get '/legacy', -> @redirect '/hello' app.options '/', -> @headers 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, PUT, DELETE' @render ''

This app responds like so:

$ curl 'localhost:4567/confs/eurucamp?bears=awesome' {"name":"eurucamp","bears":"awesome"} $ curl -X PUT localhost:4567/confs/jsconf jsconf $ curl localhost:4567/download/foo.js foo, js $ curl -iX POST localhost:4567/confs -d 'horses=fake' HTTP/1.1 201 Created Content-Type: application/json Content-Length: 17 Set-Cookie: session=XABfKjq2xvCavSitaxu0BC9XSl...; Path=/; HttpOnly Connection: keep-alive {"horses":"fake"} $ curl -i localhost:4567/legacy HTTP/1.1 303 See Other Location: /hello Connection: keep-alive Transfer-Encoding: chunked $ curl -iX OPTIONS localhost:4567/ HTTP/1.1 200 OK Access-Control-Allow-Origin: * Access-Control-Allow-Methods: GET, PUT, DELETE Connection: keep-alive

WebSocket and EventSource

app.websocket '/ws/:name', -> @socket.onmessage = (e)=> @socket.send @params.name + ': ' + e.data app.eventsource '/ws/:name', -> setInterval (=> @socket.send @params.name + ': PUSH!'), 5000

Sessions

app.get '/counter', -> @session.counter ||= 0 @session.counter += 1 @render @session.counter.toString()

Cookies

app.get '/', -> @cookie my_cookie: 'hello' @cookie another_cookie: {value: 'something', path: '/welcome', expires: new Date(2012,11,25), http: true} app.get '/welcome', -> @render @cookies.my_cookie

Helpers, EJS and HAML

app.views = __dirname + '/views' app.helpers site_name: -> 'Awesome.net' app.get '/hello', -> @ejs 'hello', locals: {name: @params.name}

# views/hello.ejs Hello , welcome to !

You can use @ejs or @haml to render a template. @render just takes a string and writes it to the response body. If your route handler returns a string, that string will be the body. Otherwise, you must call a rendering function.

$ curl 'localhost:4567/hello?name=_why' Hello _why, welcome to Awesome.net!

You can also register templates in the app code itself:

app.template 'hello.ejs', """ Hello , welcome to ! """

Context groups

You can set up a set of routes with a common prefix and add before-filters that only apply to that context.

app.get '/', -> 'Hello!' app.context '/auth', (auth) -> auth.before (next) -> if @request.headers.authorization next() else @status 401 @render 'Authorization required' auth.get '/', -> 'Secret'

$ curl localhost:4567/ Hello! $ curl localhost:4567/auth Authorization required $ curl localhost:4567/auth -H 'Authorization: foo' Secret

Credits

Created by James Coglan at Eurucamp 2012, inspired by Konstantin Haase. Released under the MIT license.

If you find bugs, you’re probably holding it wrong. Since this is such a small module, it’s almost certainly bug-free; in fact I didn’t bother writing tests because it’s obviously correct and test frameworks are ugly and stupid.