I’m happy to announce the next Spock release! This release comes with several cool new features:
- “Shareable” API definitions
wildcard
route pattern- Custom error handlers
- Custom HTTP verbs
- New CSRF protection
- New session storage system
- Removal of untyped routing
- Split of packages
- Multiple bug fixes
A big thank you goes to all awesome contributors (in chronological order): lloucas-imvu, timjb, dancingrobot84 and cdepillabout!
“Shareable” API definitions
When we started using GHCJS for a project’s frontend where the backend was powered with Spock
, a real pain point was that
calling JSON-APIs defined in the frontend didn’t really feel good: One had to render the route defined in Spock
, send the
correct JSON body (sending the correct JSON body was not enforced anywhere) and receive and parse the response JSON body. If you
are already using the same language, I think calling an endpoint from the frontend should rather feel just like a type-safe
function call. And that’s what “shareable” API definitions try to solve. Basically, the idea is to split your project into three packages:
project-api
project-frontend
project-backend
(where ‘project’ is your project’s name)
Now in project-api
we will use only Spock-api
to describe our API. For example for a login endpoint:
We can now go ahead and implement that endpoint in project-backend
using the Spock-api-server
package (together with Spock-core
or Spock
):
Note that defEndpoint
is just another Spock combinator, like the ones you already know and love (get
, post
, …).
Now we can use our API in our frontend in project-frontend
using the Spock-api-ghcjs
package:
The final step is to compile project-backend
with GHC and project-frontend
with GHCJS. The easiest way to go would be to write
two stack files, one stack.yaml
and one stack-frontend.yaml
and use the correct one. Example:
wildcard
route pattern
The wildcard
route pattern lets you capture the leftover of a route without explicitly defining what it
looks like. For example a route "foo" </> "bar"
will only match on requests to foo/bar
, but a route
"foo" </> wildcard
will match on foo/baz
, foo/baz/bim
and so on. The captured leftover will be passed
as Text
as you are used to from matched paramters. Note that a new type parameter in Path
will enforce
that wildcard
can only be used at the end of a route pattern.
Custom error handlers
You can now define your own error handler in the Spock configuration. An error handler is a function
errorHandler :: Status -> ActionCtxT () IO ()
, so you could, for example, write out the error to some
event log and show a nice error page to the user. If an uncaught exception occurs in another request
handler your error handler will be called with status500
. The default error handler will state the HTTP status
code and message along with a Powered by Spock
:-)
Custom HTTP verbs
Apart from the standard HTTP verbs GET
, POST
, … one can now write servers accepting custom HTTP verbs,
as common in some IoT device settings. You can use the hookRouteCustom
combinator for this.
New CSRF protection
While I liked the SafeAction
based CSRF protection a lot which generated custom (unguessable) endpoints for
sensitive requests on demand, it came with problems because the sessions were no longer serializable and
became quite large in some cases. Thus we now implement the common csrf
protection mechanism by providing a
csrf
token per session and (optionally) automatically checking for it in all HTTP requests that are not
GET
, HEAD
, OPTIONS
or custom. Spock will look for the token in a configurable post parameter or in a
configurable HTTP header.
New session storage system
We now allow interchangeable session storage systems, which allows you to replace the stm
based implementation
with for instance a redis
based one. This is very useful if you have a cluster of webservers running that
should share session state of users across workers. Essentially you will need to provide an implementation by filling
the record SessionStore
and passing it to the Spock configuration.
Other changes
Removal of untyped routing
The untyped routing has fully been removed from Spock
. There’s really no downside on using the type-safe api.
Split of packages
We’ve split the Spock
package into Spock
and Spock-core
. If you are using features like sessions and/or
database pooling then Spock
is the right package for you. Spock-core
is only a small package providing
the basic wiring and combinators. We are planning on splitting out more features to make them reusable in
other projects.
Multiple bug fixes
There are several bugs fixed and we’ve moved to some standard packages like cookie
instead of our homegrown
solution which should improve the stability of Spock.
Closing thoughts
Getting ready for a new release was a lot of work, but now it’s ready and I am curious to know what you think! Please
send me an email or comment on Reddit or Hacker News. We’ve also already migrated TramCloud
(sorry, German) to this version heavily relying on Spock-api
and GHCJS.