Node.js at Walmart: Plugins as the center of team collaboration
Ben Acker, Sr. Engineer, Walmart
We were talking about hapi and what I'm going to do, is to talk to you guys about the plug-in model and how it helps for team development, for like distributed teams, or teams that are working on different functionality all kind of stuff. But what I'm going to do first is go over all of the basically why that is, go over plug-in architecture a little bit and what they do.
So plug-ins first of all provide lots of different types of functionality within a hapi application you've got. It can provide anything from end points to internal APIs requested life cycle augmentations and CRUD, and let's you bind into several events, these are all things that each one of these can do. Now you can have a plug-in that can do all of these things or you can just have plug-ins that will do these individually spread out, spread out through your application, and the gateway drug into all of the—the gateway drug into this is a thing known as the Pack.
Now when you're creating—when you're creating an application, quite often especially at somewhere like Walmart, you're not just going to have something listening on port 80 right? More than likely you're going to have a few different layers, and you're going to have things listening on multiple ports.
You're going to have ports for HTTP traffic, you're going to have ports for HTTPS traffic, and I'm talking super fast. I'm just going to slow it down a little bit guys. Just a little bit, and in addition to that you might have other things, other ports that are open for perhaps internal admin work, or things that you're not exposing, and all of these are going to be different HTTP servers, right?
So, what the pack does is, the pack allows you to create all of these servers and treat them basically treat them as one entity from which you can select different servers and add routes, like if you wanted to you could add a route to all of your servers at once or if you wanted to you could add an API that's going to be accessible by everything that's routed to all these at once.
It's the thing that provides all of the—I'm just going to throw all of these up here, and we'll talk about them, but these are some of the methods. When you're creating your pack, these are some of the methods and things that you might be doing. There's the pack.server will create your servers, pack.require allows you to to add a whole bunch of plug-ins at the same time. Register allows you to register a single plug-in, and pass in options, and that will be, remember that—we'll go back to that later. And you've got pack.start, pac.stop,this allows you to start all of your servers at once and stop all of your servers at once.
And pack.allow gives you the ability to add permissions to specific plug-ins on what they're allowed to do. One of the things that Aaron was talking about was—what middlewares do is they provide function chaining so that you can do request life cycle augmentation, right? So, in hapi you have the ability to do—you have these function—these scenes that we've provided in the request life cycle that you can add stuff into, but only if you've given permissions for those plug-ins to do so.
Now the plug-in API ultimately is just this. It's either a directory within your application that's got something that implements the register method or you've got a Node package module that's sitting outside somewhere, either on Github or a private NPM, or a public NPM where you can—that has an exports or register function, and what happens, is when your pack is starting up and you've registered a plug-in with it will go in and it will call this method, it will pass the pack in, so that the plug-in can use the pac to register whatever its doing, whether it's end points or APIs or what have you.
And then the options that are going for this pack get passed in there and the next so it can move on to the next. Here are some of the—one of the things that's really cool about the pack is that when you create your servers you give them tags. So say you've got one, we'll in fact we'll go to it in a second, but you can create a server that has like an API tag with HTTP, and then you create another server that's listening on 443 that's HTTPS and secure, but both of them have API, and then you've got another one for admin.
You can say plugin.select and then pass in your tag that you've named these things and it will just select the servers that are based on that tag name so you can bulk add a whole bunch of routes to those specific servers, and they'll go in. plugin.api allows you to add in API methods that it can be used across plug-ins without having to require—without having to just have loads of cross requires across all of your plug-ins and hapi because you've got the ability to just use your parent require on hapi to call these. And that was really a dumb sentence but it made sense in my head when my brain was constructing the words.
All these are based on permissions and the plugin.events gives you the ability to tie into server events once again if you have permissions. So to show you a little bit what this stuff looks like we've got a little bit of stuff over here. Now can folks see this? Sweet. Alright, so this is just a pack. So we've got a—we're requiring hapi, where I've created a little plug-in—a lib plug-in and then we're creating a couple of servers and adding a couple of labels in there.
This is just for example. And then we're using the pack to register our plug-in and this has the call back in the event that there's an error and then we're starting up. So that's really all it is. That probably looks ridiculously familiar. You know you're making—if you've written any Node HTTP server—you create a server you create something that you're listening to, you start that sucker up except for the, obviously the file where we're calling register.
So the plug-in that I decided to do, say like you're a retail company, I wanted to provide something that provided an external endpoint and an internal API. So say you're a retail company, and obviously, if you're a retail company you've got items that are going to have some type of price assigned. You're going to want to be able to have like an external endpoint that's going to have the price available, so prices/item GET so you pass in the item ID and you get the price and then also that's going to be registered once you start it and then it also registers pack.api price, so you can call that from any other plug-in and just call price and say some of your items, I don't know if you'll like this—
Any TV can just be like a 8.66. If you want to get like Pokemon's X or Y you can get that for 18.06 those are your values that're going to be returned from this function. But once you've got all that—wow that's nice and tiny—sweet mother of science! Once you've got all that stuff running, we can then go and—
Your mom. Oh loclahost, come on guys, you didn't see that and tell me? Too small? There we go. So that's how the plug-in API works. Now I talked about the pack and what that provided and the plug-in API and how that works. Let's—come on dude, use your keys. Composer actually does something that's really cool and enables all the distribution of work made by teams with hapi. What composer does is it takes this server file that we've used to create everything and build it in there and you Eran and Wyatt were talking a little bit about our deployment artifacts. It turns those into a configuration so that turns into this. We use just one start up script that's already packaged with hapi and that's it.
Something like this plus a package.json—those are our deployment artifacts. We build off of those, it's deployed and that's where the beauty of this with team development comes in. Instead of having—the only place that we ever have merge problems is going to be in the server.json file where we're defining all of the plug-ins that we're using, because each team can work on different things. I was talking with Bradley Miller earlier, he is one of the guys on our team, and he was saying that they were doing work on a couple of different plug-ins and completely had forgotten about some of the other plug-ins that were in development by other people in the team, just because you really just don't ever have to worry about them. Everything is so distributed and Kevin is going to talking about getting doing MWEB development to get that incorporated into one of the deployments that we had, we added in a plug-in for MWEB, put it in the package.json and deployed it and that was it. It went in there and went pretty easily so I should have done that before I went back to the code. I didn't do it. But also one of the problems that once again that Eran already addressed that this can bring up is, there's loads of different teams, there's loads of different environments in QA that these teams can deploy to.
So, there's differences in configuration, like there might be different upstream servers, or downstream servers. You might be starting up with different port names, or port numbers, there might be different host names for everything. So what we've got to deal with that is Confidence. This is where Eran was talking about us starting up some A/B testing. I think, Kevin, are you talking about some of this also?
No, I'm not going to be [xx].
Alright, cool but what confidence does is it gives—so you've got these configuration files that are going to be—that have different configurations that it can choose based on environment or on other factors that you specify. And then it can be, it just makes it ends up being a lot easier than just having a bazillion configuration files that are out there that you're having to deal with.
So the other thing that this gives is it gives a launching point for A/B testing because you can have different configurations that's then evaluated on the client or server side, and then it's pretty rad. So, yes, check it out. That will be under github.com/spumko/confidence. Anyway, that is all I've got. thanks everybody.
Node in Production
See techniques for deploying a large-scale, high-uptime production cluster.