Talks

Introducing Brainstem, your companion for rich Rails APIs

By Andrew Cantino

Backbone applications.
All of this is designed to reduce network requests and simplify development of HTML5 applications, especially mobile ones. With Backbone + Brainstem, loading a hierarchy of objects from your server can be reduced to one line of code and one network request.
This talk will survey Brainstem usage in Rails, then dive into how it can enable rich mobile HTML5 applications.

Help us caption & translate this video!

http://amara.org/v/FGak/

RailsConf 2013

00:00:12.259 thank you so my name is Andrew cantino
00:00:19.800 um I work for a company called mavenlink we build a project management software and financial management software for
00:00:26.340 consultancies we're based in San Francisco and here I'm I'm here today to introduce
00:00:31.920 you guys to brain stem which is a API presentation library that we've been working on off and on for about a year
00:00:37.800 and we actually just released it as of I don't know about five minutes ago it's public on GitHub
00:00:44.399 um so yeah I'm excited to tell you guys about it so
00:00:50.280 you know so mavenlink has a really complicated data model we've for example we have projects which have tasks which
00:00:57.360 have budgets and those budgets reflected in time entries which belong to timesheets which are in reports
00:01:03.899 Etc it's a very relational product um and it's a very complex data model
00:01:09.900 but it needs to be because ultimately we're trying to build something that models business relationships in human
00:01:15.960 relationships and those are super complicated and as much as we try to dumb it down it just there's only so far you can
00:01:21.840 simplify it and we need our API to express these same relationships in a meaningful way
00:01:27.119 in a consistent way and that's how sort of brainstem evolved
00:01:33.119 um as I mentioned we started brainstem about a year ago and have worked on it sort of off and on through that time and
00:01:40.259 it was an extraction that we pulled out of our product and we've been using it for a while now and actually at this point it's the library that powers both
00:01:47.040 our internal and external apis and it's released
00:01:53.220 so I guess I wanted to start by saying what do you want in an API for your product and maybe more generally I just want to
00:02:00.240 say you know you want an API um you know you want an API for your users
00:02:08.399 so that they can get their data out of the product they can get new data into it they can transform it in meaningful ways ultimately you're building the
00:02:14.940 system for them and you need to make it easy for them to get the data in and out and that's probably pretty obvious to
00:02:21.120 you guys but sometimes it needs to be said um you also want to API for your users
00:02:26.879 users so these are people who are trying to treat your system as a platform in some
00:02:32.040 way or trying to build products on top of it and you know we hear a lot about ecosystems and it's a little
00:02:37.860 you know over spoken about too much maybe but they ultimately they are something you want to support both
00:02:44.280 because it helps your users if there's value if value has been added to your product by other people and it helps
00:02:49.379 your business too it makes your product harder to get rid of and more into people's workflow et
00:02:54.480 cetera and finally and I think maybe most importantly you want the API for
00:03:00.120 yourselves so you want to build a unified platform that you can build on top of so as your
00:03:05.940 company grows you you know new feature development can be on top of your own API and ideally it's the same API that
00:03:12.360 you both expose to your external users and use internally and that way you've really had experience with it you've
00:03:17.640 hardened it you've used it in lots of ways and you really can make strong statements about it and a mavenlink at this point where most
00:03:24.120 new feature development is on top of our public API which lets us make some really nice strong statements like
00:03:29.640 anything our mobile app can do you could do with our API because it's the same API
00:03:34.980 um and I think that's a nice statement to be able to say so I think it's you know should be
00:03:40.739 evident that you want some sort of API for your product ideally one that you can also build on top of what else do you want in that API
00:03:47.280 well I'd claim you want a consistent API which you know should be fairly obvious but
00:03:52.739 it should be consistent in various ways so things like simple things like dates going in and out I'd always be the same
00:03:58.500 format be that like ISO 8601 or Unix epochs but just pick something and stick
00:04:04.500 to it um you want IDs to be in a consistent format be those numbers or strings of
00:04:10.920 numbers or Shaws and you know other things like routes should be predictable and then the
00:04:18.239 response structure of the API itself should you know be standardized across your endpoints and be somewhat guessable
00:04:24.419 and somewhat predictable and you know for brainstem we use Json but if you're a terrible person you
00:04:30.479 could use XML I don't know why you would want to and you also want a versioned API
00:04:38.220 so upgrading apis is always a pain in the ass like I don't think that's hard to avoid both for you and for the people
00:04:45.240 consuming your API as well but like quick show of hands how many people have had an API change under you
00:04:51.000 without a version bump yeah like about a third of you
00:04:57.120 um and what about on the other side have you ever done that to someone yeah
00:05:03.320 so you know even with versioning dealing with changes in AP in an API is really
00:05:08.820 hard but versioning certainly helps and it's more or less a prerequisite and finally you you want a fast API
00:05:16.199 and what is a fast API well I claim that at least in rails of fast
00:05:21.900 API is generally fairly close to the database doing as few queries as possible and
00:05:27.300 ideally working with some of the same Scopes that you used in your models already and I'm just going to assume that you're
00:05:32.880 using something with active model made probably active record but at least something built on top of active model
00:05:39.259 along the same lines you want an API where loading associations can be done quickly and in the same request
00:05:46.020 so side loading is the idea of you know if I'm trying to get say a project and I
00:05:51.720 also want to get all the posts in that project or the participants in that project and I want to be able to do that in one request and sort of ask for that
00:05:57.900 in a consistent way um it's much it's akin to an active record if you're loading posts and their
00:06:04.560 replies you'd want to do an include statement so you so that you don't do n plus one queries
00:06:09.960 it's the API equivalent of that um you also want to avoid object
00:06:15.840 repetition so in that same example if you're loading posts and let's say they're posters which are users you
00:06:22.319 wouldn't want that full user object to be repeated for every single one of those posts in the Json response ideally you reference it by ID in some way so
00:06:29.580 that it's just so they have a smaller response and something like gzip might help you
00:06:35.580 know ideally a compression algorithm takes care of a lot of that for you but the reality is you still need to expand
00:06:40.680 that in a client so if it's a browser for example it still needs to uncompress that possibly large Json response and it
00:06:47.340 still has to parse that as an uncompressed response which can be quite large at least for older browsers quite slow
00:06:54.360 um and finally on this slide you want to allow expressive filtering and sorting in your API which again is the same goal
00:07:01.620 of getting small responses that are exactly what the user wants so you want to allow the user to tell you exactly
00:07:07.020 what data they need and only send that back so
00:07:12.960 I guess what I just talked about is sort of our motivation for building brainstem at the time that we started it we didn't
00:07:18.240 really feel like there was anything else out there that met our needs and that landscape has changed a little bit over
00:07:23.460 the last year but then I'll talk about that more later in the talk um but let me show you guys a motivating
00:07:30.479 example of um sort of from our mobile app we want to express some fairly
00:07:36.660 complicated relational data so this I mean you would think a mobile app wouldn't be that complicated and this is
00:07:41.940 just a post it's a private message between three people I'm posting saying hey guys I'm working
00:07:46.979 on my slides and there's an attachment which is a copy of the keynote file and then it's tied to a task called make
00:07:53.400 presentation and then Jeff replied saying looks like a great start and so that's a reply object
00:07:59.400 and so I want to display all of this data in a consistent way and I want to get in one request and I
00:08:05.880 really want this post object which is sort of the whole view that you're seeing here is a single post and I want its Associated data as well
00:08:13.560 um so let's look at what that data is and I sort of just said that but we have the project that the post lives in and we at least need its title we need the
00:08:20.580 post recipients at least their names the post itself with the text the attachment with the size and possibly an icon and
00:08:28.020 the name uh the task and at least the task name and then anything similar data about the
00:08:34.440 replies and there could be more than one and then serializing this relational data is tricky so let's look at a couple
00:08:40.979 ways that we could do this the I'm going to argue somewhat naive way but also probably the easiest
00:08:47.700 is to just write it out in a Json structure that has everything you need and nothing you don't so here's all the
00:08:53.820 data that was on that slide in a fairly usable structure coming over you know adjacent request
00:09:00.240 and that's great but what if I want other information too so what if there's some new attribute added
00:09:06.480 and then I need to add it to the Json and that's fine I mean it's easy to make this changes but it kind of sucks if
00:09:11.519 that's used in multiple places do I have to change my underlying API and more generally and to that point
00:09:18.480 like if I'm using this in lots of places then it has to be customized and have the superset of all those different fields that all these different places
00:09:24.660 need and what if I'm interested in lots of posts at the same time now there's gonna
00:09:30.300 be a lot of repetition because that project title there's lots of posts in the same project so that title is going to be repeated the user information and
00:09:37.560 obviously we probably need more than I was loading is going to get repeated for all the posts and the recipients Etc
00:09:42.959 and I think the most important point is what happened to my awesome relational model you know that I built so that I could
00:09:49.320 represent this data in a consistent way you know I use rails for a reason I use active record for a reason and at least
00:09:55.200 in our app it's super relational we use a relational database and that's exactly what we need and we've lost that behavior
00:10:01.920 so let's look at another option in this option we reference everything by Hades and this obviously is a subset
00:10:08.100 of the Json so recipient IDs attachment IDs Etc and we obviously we can flesh this out
00:10:15.060 so this is a post that was the post you saw it was id25 and then it had one reply id9 so let's add that
00:10:22.860 the reply is also just a post it's you know it's a dissociation between a post and a post so it gets
00:10:28.080 added to the same top level hash Etc and then of course you have all the other objects that we saw
00:10:33.959 and and this is by the way is an example of side loading here ideally we did a
00:10:39.300 single request asking for the Post told it what associations we wanted and they all came back
00:10:45.180 um now how should we feel about this it is I think you agree it's a longer at least in this example there's a lot more
00:10:51.060 top level objects potentially we're returning more data but I claim it's far more reusable
00:10:57.600 I'd also claim that it's much more consistent because you can predict what these objects are going to look like you'd like you know what their fields
00:11:03.060 are going to be et cetera there's no like project underscore title anymore you just have a project Association you look for its title
00:11:09.540 and if you return a lot of data I don't even think it's going to be longer because it's going to take advantage of
00:11:14.700 the fact that a lot of these IDs overlap and again more importantly we got our
00:11:20.399 awesome relational model back which is what I wanted so
00:11:27.480 um now I'd like to dive a little deeper into how you would generate a Json response like that and that's what brainstem will help you do
00:11:35.339 um and in brainstem you know you actually can get all that in one request you'd make a response in this case I don't know if people in the back of the
00:11:40.980 room could see it but you're just making a response making a request to the posts API endpoint for id25 and you're asking
00:11:48.180 it to include the project user recipients task attachments replies
00:11:53.339 and you could obviously set a default for that if that's the view that you think most users are going to want so that relational structure that you
00:12:00.779 guys saw was serialized from active record objects using brainstorm presenters
00:12:06.060 so what's a presenter a presenter takes an object and in this case an active record object
00:12:12.720 and a presenter itself is a class or method that serializes that object and outputs the fields that you ultimately
00:12:18.480 want to send over the wire so in our case Json but you could use something worse
00:12:25.079 so let's look at an example presenter so this is a brainstem presenter it
00:12:31.200 inherits from brainstem presenter it's versioned you might notice that the module whoa
00:12:39.060 you might notice that the module is version V1 and if you change that it would actually
00:12:45.120 change the version of this presenter you'd find that in a different scope inside a brainstem so here we're presenting post and it
00:12:51.540 just has one method called present which takes a post and returns a ruby structure of the data that you wanted
00:12:56.880 simple um now I want to show you how you would
00:13:02.399 add associations to that because that's where it gets interesting so using the association keyword you
00:13:07.500 just list the associations on the model that you want to expose to your end users so anything you list here they're going to be able to request and
00:13:14.040 obviously you also need a presenter for any of those objects and these will get serialized as you saw earlier respectively as like reply IDs
00:13:21.959 task ID attachment IDs user ID Etc and the recipients is an interesting
00:13:27.600 case because brainstem would infer that these are recipient objects but in reality they're user objects so you need
00:13:33.720 to tell it what key to use and as I mentioned you'd need to build
00:13:39.060 presenters for any other object so here's a user presenter which looks very similar and the user the one thing we've
00:13:44.399 declared is you could ask you could ask for posts by default you won't get associations back because they could be
00:13:49.500 huge but this is the things the user could request so let me now show you real quick how
00:13:56.339 you would use that from a rails controller so here I have an API V1 post controller
00:14:02.160 which is just inheriting from application controller but there's no reason you couldn't and you probably should inherit from API controller and
00:14:09.180 put all your authentication in there in any other shared behaviors like overriding what it means to have a 404
00:14:14.519 Etc the only key thing is you have to mix in brain stem controller methods and that
00:14:20.279 gives you the present method which takes the name of the posts it's actually the table name here of posts but it could
00:14:26.399 also take an object itself and you give it a scope in the block which is the scope from which all
00:14:31.440 further brainstem modifications are going to take place so brainstem works on aeroscopes that's the only thing it understands so you stay close to the
00:14:38.639 database so here in this example I'm just giving a post unscoped with no restrictions well there's no reason you
00:14:43.740 couldn't give it posts relevant to the API or posts visible to create user Etc that's where you have to deal with your
00:14:49.920 authentication your authorization and then by doing this for free you get
00:14:56.040 the inclusions that I showed you a minute ago you also get only queries so you can make a request to post Json only
00:15:01.860 five eight twenty five and you'll only get things with that ID back and you get pagination
00:15:08.579 and then and this is an index action but this works on all the other credit actions as well
00:15:13.620 and then we also get filters and sorts so filters allow the end user to control
00:15:18.779 exactly which data they get and sorts allow them to configure the order of that data
00:15:26.579 so here's a quick example of sorting we're back in a presenter again and this
00:15:31.620 is actually kind of a silly example because we're just sorting on we're declaring that two columns are sortable updated out and created at and in this
00:15:37.920 case they're just the exact same name it's those column names in the database but the sort order method can take a
00:15:43.620 Lambda and you could do anything you want inside that as long as you're returning a scope at the end and then you can also declare a default
00:15:49.560 sort order and then the user if they wanted you to return a sword that's not one of the default or not they the default sort
00:15:56.459 they just tell you the order they want it in and then this will honor that
00:16:03.199 now probably more interestingly on filtering here's again in the presenter here's a
00:16:09.899 filter this is a filter on task ID so we're in posts and this allows the user to ask for only posts associated with a
00:16:16.260 certain task so here what happens with a filter is it
00:16:21.660 takes us takes a Lambda and is given a scope and returns a scope and then you can do anything you want inside there to
00:16:27.420 restrict the data so here we're just given the user is sending us task ID as a get parameter
00:16:34.019 and we're just passing that right in and SQL escaping is obviously up to you we try we try to help but you need to think
00:16:40.440 about what's going into your database you can also defer to Scopes defined on
00:16:46.079 the underlying class on the active record model so here popular is a scope defined on the post model which takes
00:16:54.779 one argument which is true false and then you can just defer to that so if you have other Scopes you've already used all over your product and are well
00:17:00.899 tested you can just defer to those just obviously make sure that you're basically whitelisting here you're declaring Scopes that you're willing to
00:17:07.260 expose to end users um now Scopes can also or filters can
00:17:14.760 also have defaults which is useful when you have negative Scopes or you want a scope to always be included here this is
00:17:20.819 a filter that says include private posts the default is true which means this filter always runs so the user doesn't
00:17:26.220 have to ask for it unless they don't want private posts um it's also handy if you want a scope
00:17:31.919 to always like whatever that code is to execute on every request
00:17:37.679 so I've shown you how to make a simple API in brainstem and there's obviously a lot more to it but we have exposed filters
00:17:44.760 and sorts I've shown you how to do include queries how to make a basic controller that would respond with your
00:17:50.100 data that's been presented um and now I'd like to show you
00:17:55.380 potentially how to deal with it on the client side which I think is fitting for this conference given how many talks there
00:18:01.140 have been about sort of client and server apis and I didn't know that going in but I think we're all sort of solving the same problems at the same time
00:18:09.240 um this is probably not something you would give to your end users although you certainly could and there's no reason
00:18:14.580 they couldn't use it if you set things up that way but I'm encouraging you to develop on top of your own public API
00:18:22.140 and so this is what we use when we're developing our views and we use backbone
00:18:28.500 um and you know you have this API you've built it for your end users you should use it for yourselves as well you should
00:18:33.539 eat your own dog food and live on your own platform so because we use uh backbone we wanted
00:18:40.020 to combine brain stem and backbone which gave us brainstem.js which is a adapter shim for backbone to
00:18:48.539 let it do relational model loading over brainstem so it's a little bit like Ember data in
00:18:53.700 that it preserves these relational models over the wire and it's I'm not aware of anything else well that's not
00:18:59.039 true there is a relational library for backbone but we looked at it and it didn't handle the relations in the way
00:19:05.039 we wanted for The Way We Were transmitting these Over The Wire um and in addition it would have no idea
00:19:10.260 how to talk to a brainstem API so I'm just going to give you guys a really quick example in coffeescript on
00:19:15.360 how you would consume um your brain stem data in your in your
00:19:20.820 view and as I mentioned this is actually how we're developing most of our new code is against this an API and using
00:19:26.520 brainstem.js so the first thing you do is you make a storage manager and the storage manager
00:19:32.520 is in charge of keeping track of the different models and collections that can come over the wire and keeping track of how they talk to each other
00:19:39.000 and it's at the moment as a global Singleton expected to be found in base data but we're working on refactoring it
00:19:44.880 so that you could have more than one although it that hasn't really bitten us yet because ultimately you usually want to share this across different views
00:19:51.780 the storage manager is also an identity map which means it keeps track of the mappings between object types and their
00:19:56.880 IDs it makes it easy to quickly look up things by ID and more importantly to not
00:20:02.340 re-request things that it already has so the you know in the beginning we built this for our mobile app and we wanted to
00:20:07.860 be sure that the requests that went over the wire were small and quick and we didn't want to re-request things the
00:20:13.500 user already had so that was sort of part of it that was part of the design principles that went into this
00:20:19.380 so in this case in this example we have a storage manager we're telling it about posts projects and users and which
00:20:24.900 backbone collections to find those things in and then I'm going to show you how to use the storage manager and there's kind
00:20:31.440 of a lot here or I'm sorry this is actually just a post so real quick this is how you declare the associations
00:20:38.760 so you use in copyscript the at sign means clot in this context means a class
00:20:44.760 method and this is actually class data so you declare associations on the post
00:20:50.160 or on the model then you extend your model from brainstem model which itself is just a light extension on top of
00:20:56.160 backbone model so here are in our post model and obviously there'd be a lot more methods that we'd probably want to actually use
00:21:02.280 this but we have projects we have a project which is findable in the projects
00:21:08.100 collection we have many replies which are actually posts we have a user in the users collection recipients also in the
00:21:15.120 user's collection Etc and the syntax here is if you have just a literal string it means up has one or
00:21:21.240 belongs to and the array means has many and from that brainstem can the
00:21:26.400 brainstem JS can figure out where to look for things so and then let me show you how you
00:21:32.100 actually load this and here's my example which has a lot going on but if we just focus on the collection load
00:21:38.039 all we're doing is we're tailing the storage manager to load some posts it's going to be one page of posts using the
00:21:43.380 default pagination because we haven't said otherwise and you wanted to include the project user recipients attachments Etc
00:21:49.799 and this is otherwise just going to end up as pretty standard backbone where you get a collection object it won't have
00:21:56.039 any data in it yet unless this is already in the storage manager so it'll either be loaded right away if we have the data or it'll be async and you have
00:22:02.700 to wait for the reset we also add an event called loaded which you can bind to directly and happens late which is
00:22:08.159 usually what you want alternatively you can give this a success callback or just change success callbacks
00:22:15.380 the rest of the code here is just mostly boiler put boilerplate to deal with this we bind to the collection we've reset
00:22:22.080 and remove at all is it actually on the slide but it just renders all the posts on the page
00:22:27.299 and then when we render if the collection is already loaded which would likely be because we've refreshed we
00:22:32.340 already have had this page in the workflow already you just render everything otherwise you know show a spinner say just a moment Etc while the
00:22:39.480 data is coming over the wire the goal of the storage manager here is to tell you when all the data you need
00:22:45.299 is ready so you declare to the storage manager everything you're going to need I need these posts and I need these associations and when it says the data
00:22:52.200 is loaded you can just trust that it's there and you don't need to worry about where it got it maybe half of that came over the wire and half was already in
00:22:58.260 memory it'll take care of that so the storage manager of course can also load single models here we're
00:23:04.799 loading users a user with user ID and including its posts and you can also of
00:23:10.140 course run filters sorts anything else that you can do at a brainstem API
00:23:15.299 so obviously you don't have to use backbone and many of you probably don't but you might as well if you're going to use brain stem and you use backbone you
00:23:21.720 might as well play with this otherwise there's no reason you couldn't consume the brainstem API over some other format
00:23:27.120 and hopefully people will contribute other libraries for it or you could just use
00:23:32.280 it directly on the Json so to quickly rehash
00:23:37.740 brainstem JS extends backbone with relational models and it ensures that any associations you
00:23:44.820 need are available in identity map by the time it tells you that it's ready
00:23:51.000 um and then let me show you how you'd actually use these relational models so it looks just like backbone the post get user get name would be get
00:23:58.500 the name of the user on the post and under the covers What's Happening Here is the storage manager is
00:24:04.020 inspecting the post looking at the user ID going to the user's collection grabbing the appropriate user and then changing
00:24:10.200 on from there so it's just a lightweight wrapper with a super call or around the basic
00:24:15.659 backbone Behavior you can also also do that with has menus as we're doing with replies here
00:24:24.780 so you know brainstem evolved over about a year as I said and we went through a couple different iterations of it so I
00:24:30.720 just wanted to touch upon a couple design decisions we encountered and sort of challenges that I think you guys would find interesting you might find
00:24:37.260 this useful for stuff you're building or just humor me
00:24:42.480 um so let's see the first one is that we use IDs as keys
00:24:49.200 so many apis return arrays and we made the conscious decision to return hashes
00:24:54.840 instead so as you see here this is the posts and the IDS are actually strings
00:25:01.500 the keys are strings of the IDS now the keys have to be strings because keys and Json are strings it would be
00:25:08.159 kind of nice to be able to use numbers but that's not how Json works and it actually turned out to not be a bad thing because it means it's really easy
00:25:14.640 to change how you treat IDs you could turn these into Shaws and no one's gonna be making the assumption that everything's gonna be an integer plus
00:25:20.340 maybe it's huge maybe you're overflowing JavaScript so I think that ended up working well
00:25:27.240 um Additionally the one reason we did this is because when you're traversing the structure that comes back following
00:25:32.580 associations it just doesn't make sense to be iterating over arrays searching for IDs like most apis return with
00:25:38.940 arrays of things and you have to search them and we found we were also doing that and that just felt a little silly and we don't want to encourage that
00:25:44.940 behavior because you should be order one instead of order n on something you're doing repeatably additionally we just want users to treat
00:25:51.779 this as a lookup table this API response is structured like a lookup table it's almost like a database slice and we just
00:25:56.820 want to structure it that way to encourage that behavior but um one question that obviously would
00:26:03.900 fall out of this is fine so it's a hash how do you handle sorting because you certainly shouldn't trust sorting on
00:26:09.059 hashes and I know Ruby one nine has ordered hashes but I'm not sure that's a good idea
00:26:14.159 so that brings us to the results array which has a couple properties it's just a list of the matching keys and IDs that
00:26:22.080 match the user's exact response because it's quite possible that you're in this case posts your post hash could be a lot
00:26:28.679 larger than a response set because posts can have replies which are posts so you have a poly well you sort of you can
00:26:34.679 have polymorphic associations and here you just have same object the same object associations
00:26:39.840 so you what you do when you actually want to look at the result set is you just Loop over the results looking up each one in its base object
00:26:46.559 and that's all there is to it and you know if you use brain or back brainstem JS it'll do it for you but it's also not
00:26:52.440 very hard as you can see we've turned all IDs into Strings globally so that things are consistent you don't have to think about
00:26:58.380 it um it also means the results themselves can be polymorphic you could come back
00:27:04.260 with if it were for search results you could come back with users and assets and stories or whatever else
00:27:09.480 because they're they have a type effectively so
00:27:16.620 I think I basically just said this allows you to declare exactly what results match the order and sort
00:27:21.720 explicitly and it allows you to do sort of mixed type responses
00:27:28.500 um and then another interesting decision we made is how we that we put filters in
00:27:34.380 the presenters so usually presenters are just in charge of turning data or taking an object and presenting it getting it
00:27:39.840 ready to be sent over the wire and what we actually end up declaring the valid sorts and filters in them as well and
00:27:45.299 probably more things in the future and most other libraries don't either don't handle filters and sorts or they
00:27:51.299 let you they just leave them up to you to handle on your rails controller by changing scopes
00:27:57.720 um and we wanted to head in a different direction and the reason we did that was is twofold one is if we're if we're
00:28:03.779 versioning the presenters it kind of makes sense to version these other behaviors at the same time and it makes it easy to upgrade you just
00:28:10.200 you know use subclass or duplicate possibly the code in the presenter um and you can that's a good time to
00:28:16.559 change how filters and sorts work in their defaults um that's that and then also
00:28:22.500 it's nice for brainstem to know when looking at an object how it can be filtered and sorted without having to
00:28:28.799 infer that from like your controller and the reason we want to do that is because down and this isn't done yet but down
00:28:34.200 the down the line we want to allow you to do full recursive loading of objects so this is something that the Facebook
00:28:39.840 API does LinkedIn does to some extent it'd be really nice to be able to declare say I want this project and I
00:28:45.900 want posts that match this filter and from those posts I want users to match this filter et cetera just be very expressive and really the only way to do
00:28:52.620 that is to know that is for the brainstem to be able to look up filters easily by object type
00:28:57.659 so this is the direction we want to head and it's you know we think it's a valuable addition to the library because it's not something you'd want to build
00:29:03.539 yourself you know so it's a good thing for it to be boiled into the library for people who want it and obviously you
00:29:08.940 want to be careful because your users could ask for huge cross-sections of data but on the other hand if they need
00:29:14.520 that data and it's their data you'd rather them get it in one request and they'll tell you exactly what they need instead of hammering your database or
00:29:20.520 your system with lots of requests now speaking of other libraries I just want to talk briefly about some of the
00:29:26.700 other libraries that have started to show up this year explicitly active model serializers I think is great
00:29:33.899 um it I'd say it's more mature than brain stem in terms of its DSL for specifying fields we declare a ruby
00:29:40.559 structure and active model serializers has a more explicit DSL which probably allows for better introspection and
00:29:46.919 maybe in the direction that we'll head however it doesn't do filtering or sorting or pagination because it's just
00:29:52.679 in charge it cares only about the presentation and we care about also adding a bunch of API behaviors so you
00:29:58.500 don't have to worry about them so basically what we add on top of that is
00:30:04.039 everything you'd want in the API and I think there is definitely some
00:30:09.059 collaboration here there these I don't know if these can be combined if just we could share ideas but both of these have
00:30:14.700 sort of evolved independently over the last year um it's also worth mentioning as I did
00:30:20.039 earlier Ember data which I think is fantastic and Amber is very cool but you know we've built most of our site on
00:30:25.740 backbone I personally like backbone for its sort of I find it's very readable it's very easy to understand the whole
00:30:30.779 code base et cetera number is much bigger um and brainstem.js in a lot of ways add
00:30:36.840 some of the behavior from Ember data to Backbone so it might be possible these brainstem.js without brains without
00:30:42.480 brain stem if you just want relational models we haven't played teasing that apart yet
00:30:47.760 and again I think there there could be some interesting uh collaboration between these two libraries
00:30:53.340 so to summarize um brainstem is a presenter library for
00:30:58.679 serializing your models it's also an API abstraction layer for
00:31:04.500 building powerful apis and it has an optional adapter for backbone if you want to use it which is
00:31:11.940 what we ended up using a lot of and if you use brain stem then it should be easy to make your API user sortable
00:31:18.720 filterable with side loading of associations which we think means shorter response times fewer requests and hopefully
00:31:26.340 happier Developers so we've launched it uh a few minutes
00:31:33.480 ago you should check it out and uh these slides are online you should follow me
00:31:39.539 on Twitter and send me any questions you have I'm tectonic on Twitter or you can ask me after the talk or I think we have
00:31:44.880 some time now so do you guys have any questions