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:
GET, POST, PUT,
DELETE, PATCH, HEAD, and
OPTIONS:named and
* matchers$ npm install berliner
app = require 'berliner' app.get '/', -> 'Hello, world!' app.run 4567
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
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-aliveapp.websocket '/ws/:name', ->
@socket.onmessage = (e)=>
@socket.send @params.name + ': ' + e.data
app.eventsource '/ws/:name', ->
setInterval (=> @socket.send @params.name + ': PUSH!'), 5000app.get '/counter', -> @session.counter ||= 0 @session.counter += 1 @render @session.counter.toString()
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_cookieapp.views = __dirname + '/views'
app.helpers
site_name: -> 'Awesome.net'
app.get '/hello', ->
@ejs 'hello', locals: {name: @params.name}# views/hello.ejs Hello <%= name %>, welcome to <%= site_name() %>!
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 <%= name %>, welcome to <%= site_name() %>! """
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
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.