[Hosting and bandwidth provided by the Blue Box Group. Check them out at BlueBox.net.]
[This episode is sponsored by Component One, makers of Wijmo. If you need stunning UI elements or awesome graphs and charts, then go to Wijmo.com and check them out.]
JAMISON: We know you have another family. But we love you enough to take what we’ll get.
CHUCK: [Laughs] I’m going to leave that in. I live in Utah.
CHUCK: Alright. We’re actually recording this on the 17th of December. Give away all of our secrets [inaudible] come out.
JAMISON: You live in Utah. You’ve got another family, recording it on the 17th.
CHUCK: Yeah, this should come out around New Year’s.
AARON: Chuck’s not lying.
JOE: This place is a sham.
JAMISON: Opening the kimono today. The kimono is wide open.
CHUCK: Yeah, we’re like three weeks ahead. This is why I’ve been so confused about episode numbers for the last three weeks. Anyway, this week we’re going to talk about JSON APIs. Now I don’t know anyone here who might have, I don’t know, recently done a course on this kind of stuff. This week on our panel, we have Joe Eames.
JOE: Hey everybody.
CHUCK: Jamison Dance.
JAMISON: It’s almost the end of the year, Luigi. Just think about that.
CHUCK: AJ O’Neal.
AJ: Yo, yo, yo, coming at you from the past, going back to the future.
CHUCK: Year of Luigi. Aaron Frost.
CHUCK: I’m Charles Max Wood from DevChat.TV. And this week, we really are going talk about JSON APIs.
CHUCK: So JSON APIs. The concept’s pretty simple, right? You make a request to server, you get JSON back.
JOE: Yeah, that’s why it’s so easy to implement.
JAMISON: Do you want to get into the philosophical debate about why you would do this at all?
JOE: I do.
JAMISON: Or do we just want to assume you have decided to do a JSON API?
JOE: No. I think we should
CHUCK: Because we hate XML, that’s why.
JOE: But wait a second.
JAMISON: Well, why not do a server side app?
AARON: There are three good questions in there, though.
AARON: Like why not XML?
JAMISON: I only noticed one, so [chuckles] yeah, go ahead Aaron.
AARON: XML versus JSON.
AJ: XML, of course.
JOE: I’m pretty sure you can’t even do JSON because it’s the XML HTTP Request object, right? So it only does XML.
AARON: Yeah. It would be wrong.
AJ: Yeah, mostly just malformed XML actually.
AARON: Yeah, unfortunately.
AJ: Strict XML would cause a parse error. It’s got that little exclamation right in the front.
CHUCK: So are there drawbacks to XML other than the fact that you can’t read it and it’s a little more verbose than JSON?
JOE: That's not all the drawbacks. It's too big, way too big.
AARON: It's way too big, yeah.
JOE: Sucks to read.
AJ: There's no representation of objects, arrays, strings, or literals.
JAMISON: There are. There…
AARON: It does have…
AJ: See, see, you don't know.
AJ: How do you represent an array in XML?
JAMISON: The answer is however you want.
JOE: Collection. Do a collection of anything.
AARON: Yeah. Same when you do like an unordered list in HTML.
AJ: So, I’m just going to put <li> in front of my item and that’s going to make it an array?
AARON: If that’s what you call the thing, yeah.
AJ: So basically, there’s no standard way to do it.
JOE: I think the point you’re trying to make, AJ, is more that XML conveys less about the content than JSON does.
AARON: That’s not always true, because the XML tag itself, you could put attributes on it the same way you could attribute an HTML tag. So it’s got some things that JSON doesn’t have. You do it a different way in JSON though. You get the same functionality, just in a different way, right?
JOE: Yeah. So, maybe the right way to say that is that it requires a little bit more understanding on part of the sender and the receiver.
AJ: Maybe the correct way to say it would be it’s a markup language, not an object notation.
JOE: Yeah, but that’s…
JAMISON: I think XML versus JSON is a good illustration of the value of constraints because JSON is a much more constrained format. But if you let people do whatever they want, then I will misunderstand what the correct way is to do stuff and I’ll do something dumb.
CHUCK: So I’m the consultant here. I’ll give you the right answer. It depends. But use JSON.
AARON: Yeah, one of the worst talks I ever listened to was at the Utah Java User Group, UJUG, and a guy came in and spoke about why JSON sucks compared to XML. And I was a new programmer so I didn’t dare to speak up but I was just like, “There’s so much wrong about what you’re saying.” I’ve learned since then that he was just totally wrong. XML, it’s not easier to read. It’s not more concise. It doesn’t go over the wire better. It’s not better. The size of the file you’re sending, it’s not going to be inherently better in XML versus JSON. It’s just not.
CHUCK: In my opinion, I think the capabilities between the two are approximately the same. It just boils down for me to the ability to look at JSON and know what I have versus looking at XML and it’s more verbose. And so I actually have to spend a little bit more time looking at it to get the same amount out of it. And that’s not to say that occasionally you don’t wind up with a big JSON blob as your response. But for the most part, most JSON responses I see are pretty simple and pretty easy to read.
JAMISON: It’s the Year of Luigi, you guys. It’s 2013. I think we’re all in agreement that we should use JSON.
CHUCK: What do you mean by the Year of Luigi? Where is that coming from?
JAMISON: It’s the Year of Luigi. Nintendo declared 2013 is the Year of Luigi.
JOE: Oh, cool.
JAMISON: They also said the Year of Luigi will continue to 2014. So, the Year of Luigi is two years.
JOE: That’s fine.
AARON: It’s Two Years of Luigi.
JOE: It’s fine because it’s obviously not the Year of Wii U.
JOE: Whatever year it is…
CHUCK: The Year of Luigi 1-upped somewhere along the way.
JOE: Alright. So then, why not a server app?
AARON: It goes beyond just the single-page app too, though. Because when you write
JOE: Yeah, that’s extreme limit over there, single-page app.
AARON: When you write one app that uses a JSON backend, you now, implementing a second app, if you wanted to go do a native iOS app or an Android app, there’s already a backend in place for it. So now you’ve got two frontend apps consuming the same backend. It makes it a bit easier to spread into different zones if you write it this way.
JOE: That’s also a big maybe, frosty, because if you don’t design it so that it will work with both ends then it’s not going to. But you can’t possibly do it the other way.
JOE: Without a JSON API, you can’t do it at all.
JOE: The likelihood is even if you try to do it the right way, you’re still going to get it wrong and have to rev all of your APIs for your iOS app because it’s just hard to get two different frontends working with the same backend.
AARON: It is. I agree. You get some reuse there, though. At least you get a start.
AARON: You’ll have to customize some bits though, but I think you’re right.
JOE: It’s more a fact of the matter that it’s just hard to put two pieces together. It’s hard to reuse code. And [inaudible] gives you a chance to do it. It just is hard to reuse code and a vast majority of people are going to find that, “Ugh, there’s so many things that I need to think about.”
AARON: Yeah. But some of those, you move into the new one, you build some new functionality into it and your other apps can take advantage of it too. So I don’t know. It’s kind of nice. And along the way you learn what you should and shouldn’t do for when you do the public API if you have one, right?
CHUCK: Yeah. One other thing that I want to point out with this is that we kind of moved part the way along this continuum when Facebook introduced their Facebook apps. Because effectively what you were doing is you were consuming their JSON API to either build in elements on your page or you were using it to add functionality and pull data from their service, in a lot of cases both. And so you really can enhance a completely different website off of the API of your main app or build out separate apps based off of the same backend. And so it really does give you a lot of flexibility doing it the way that you’re talking about.
JOE: Absolutely. There’s another reason, too, is often it will appear to be a lot more performant to the user.
CHUCK: Yeah, I’ve heard that from a couple of people and the main premise that I always hear with that, you have to understand that I’ve spent the last seven years building the server side app or the load the HTML, click a link and load a new HTML page app. And the argument that I’ve heard is that you offload a lot of that rendering and work to the browser. And so effectively what you’re doing is you’re turning your app into a distributed app.
AARON: Yeah. [Inaudible] throwing out all merits of doing a server side rendered app too, though, which I think there are some. I’m not going to preach it, but I think there are some site that it makes sense to have server rendered content.
JAMISON: I think a lot of it comes down to the tools in the workflow almost, just as much as the specific project. If you want to do a server side app, you can crank that out pretty fast in Rails. And I’m sure there are tons of tools in other languages that are just all optimized for it. So sometimes it’s the path of least resistance if you’re developing it.
AJ: Now that’s absolutely true. I feel like it’s a lot more expensive to build a client side app than a server side app today, probably an order of magnitude.
JAMISON: Yeah. Like, “Hey do you want real modules in your code? Cool. Use a server side language and then you have that.” There are some weaknesses to the tooling and development environment of client side stuff. But they’re all getting better.
JOE: Okay. So, that was the why not a server app. What was the other question?
AARON: Have any of you guys designed a solid large JSON API, like a big REST API with multiple buckets and stuff?
JOE: Big no.
CHUCK: No. I’ve built several little ones. I’ve been involved in a few, but it was just build this endpoint, build that endpoint. I do want to talk about REST for a minute, because that’s a term that gets thrown around a lot and I don’t know if people really understand what it’s about.
JOE: REST is simple. There is only one person in the world who does REST. It’s the guy who invented it. Nobody else does.
AARON: [Chuckles] So cynical.
JAMISON: REST is like religion. No, it really is. It’s so easy to just condemn everyone else for not using the same definition of REST that you use.
AJ: I want to throw out a definition of REST.
JAMISON: Yeah, go for it.
AJ: REST is that you can give someone a link to something, whether it be an HTTP link or even a native application type of link, like Spotify does for example, and it will bring them to the application in the same state in which you view that application. And that entities in that application can be operated on with a defined convention within that application that has create [inaudible]
AARON: AJ’s gone.
JOE: That sucks, because I was going to completely WTF on that definition.
CHUCK: Yeah, I was too. [Chuckles]
AARON: Has anyone else had any experience building a large JSON API?
JOE: Large JSON? Sure. Well, I’ve built a couple of demos, probably by far the biggest. I participated in building that. I certainly didn’t design it from the ground up by any means. In fact, I just stepped into existing requirements and RESTful was not a requirement by any means. So it was a very non-RESTful API. But it really mimicked other APIs that I’ve done in other languages that were not JSON. XML, whether it’s JSON or not, a non-RESTful API to me feels a lot the same.
JAMISON: So, what do you mean when you say you have a non-RESTful API? We missed the definition, so I’m just going to go ahead with you post to create resources. You get them by an ID after, their base URL or whatever. You just interact with them in terms of resources through URLs.
JOE: And one of my favourite things when you create resources is that an update a POST or PUT.
JAMISON: It’s PUT and I’ll fight you.
JOE: Which one’s PUT?
JAMISON: PUT is the update. You PUT to the resource/ID and you POST to the resource to create new ones.
CHUCK: That’s what Rails does.
JAMISON: Yeah, that’s the thing. The definition of Rails that most people have is what Rails does, or of REST that most people have is what Rails does.
JAMISON: Which is fine.
JOE: Certainly not universally agreed upon though.
CHUCK: No, it’s not.
JOE: So, my definition of REST would simply be that four HTTP verbs and you’re operating on objects to just make changes to them. You’re either updating, creating, deleting, or sometime in the future the magical unicorns of patch will become available.
CHUCK: Yeah. I think it’s important to have a convention that you follow or some kind of standard that you go for.
CHUCK: And it really helps then define where different things lie. One question I have for you guys about REST is if you have certain kinds of updates or mass updates or things like that that you do, do you use the POST or PUT that’s requisite for that or do you actually create a separate endpoint for some kind of specialized operation?
JAMISON: We’re pretty aggressive about throwing away REST to save performance. So we use it when it’s convenient and helpful. But if there’s some task where it would just be a ton of calls to do something if you’re strictly RESTful then we just make a special endpoint for it.
JOE: So Jamison, I think I only kind of answered half the question. So if you’d answer the other half, what does a non-RESTful API feel like?
JAMISON: Anything else? I don’t know.
JAMISON: A lot of times it can feel like you’re doing RPC with URLs as the function name and parameters.
CHUCK: Kind of like SOAP?
JAMISON: I’ve never had the fortune of working with SOAP.
JAMISON: Yeah. Usually, you want to keep some sanity though, right? If you're deleting stuff, even if it’s not going to fit into a RESTful URL. It’s too [inaudible].
JOE: Yeah, there’s no accounting for stupidity. But the point is that non-RESTful URL you could do something like. I’ve seen some fairly crazy endpoints implemented in ways like that. Like you have to use a POST to do a GET, to get some data and stuff.
AJ: So I would say that it’s not necessarily unrestful to, HTTP can’t do everything. Sometimes you have to do a POST to do a GET because you need to POST a list of IDs and that list is rather long and so it doesn’t make sense to put it into a GET because you need specific parameters that are nested attributes. And so you have to do a POST as an HTTP verb in order to specify that I wouldn’t say that you’re losing the spirit of RESTfulness.
JAMISON: The spirit of RESTfulness is actually the fourth spirit of Christmas.
JAMISON: It’s past, present, future.
JOE: And REST?
JAMISON: And REST, yeah.
CHUCK: [Laughs] Oh.
AJ: But when I think not RESTful, I think an API that hasn’t had any design consideration. Like when you go to view an item, it’s /user and when you go to edit the user, it’s /node47/user-update-now. Or you now, there’s when people just tack on a lot stuff to an API without considering the operations that are going to be performed on that entity as an object.
JOE: I wish that was…
CHUCK: Have you been working on WordPress lately?
JOE: I wish that was my definition of what RESTful and non-RESTful was. And I certainly don’t feel like the general industry as a whole agrees with you, with that, AJ.
AJ: Where would you say that what I said was incorrect that that is not RESTful?
JOE: I’m only going by what I feel like the industry as a whole says. The industry as a whole says REST is GET, POST, PUT, and DELETE on raw entities.
AJ: But REST doesn’t, HTTP is an implementation of REST. It’s not what REST is.
JOE: I’m not disagreeing with you. I’m just saying what I believe the industries’ definition at large to be, what most people consider it to be. So when you say just a well-designed API is RESTful, even though if it’s a call, an RPC type call, I would say I disagree. That’s what most people think.
AJ: No, I’m not saying RCP type call is RESTful. I’m saying that you have a way to access resources and a standardized way to act upon them, like that you can delete something, you can add something, and you can share that usually with a URL except in those rare cases where you can’t because HTTP doesn’t have a way of supporting doing everything with GET.
JOE: My opinion is I could get [inaudible] with that entirely and have it be completely non-RESTful. I could meet your requirements and have it be, that would be my opinion. But again, I don’t think that’s right. I wish that it wasn’t right. I wish that RESTful was just a piece of what you would consider to be a well-defined API. I think as an industry we haven’t come to any kind of consensus about that. We have this Nazi view of what REST is and nobody does it.
AJ: Well, the way that Rails does it is pretty much, the first implementation of RESTful web design was more or less, the first popular, the thing that brought REST to light was [inaudible]. Yes or no.
JOE: I’ll defer to Chuck on that.
CHUCK: I would say mostly yes. But even then, it was based on a PhD dissertation. I don’t remember the guy’s name. And Rails didn’t even get it right. [Chuckles] So for the most part, it is a pretty well-defined set of conventions around a resource. And so your resource can be a user, can be some other object, and then your different GET, POST, PUT, DELETE, define different operations around that resource. And I think no matter how you do it, again if you have that convention that people can follow. And the thing I like about REST is if I say REST to somebody else, then they can generally guess or figure out pretty closely what I’m doing.
JOE: Yeah, and I would say that most today wouldn’t have a definition like what AJ said. Even though I like AJ’s definition, I would say most people would not agree with that.
CHUCK: Yeah, I agree with you on that. There’s enough convention around it for most people to where they would approach it in a particular way because they know that it works in a particular way.
JOE: But I also think that like Jamison said, when you come to actually building a real production type application, you quickly realize that REST has a whole bunch of holes and so you start either making
CHUCK: Right, you start deciding which rules to break.
JOE: Yeah, exactly.
CHUCK: And you make intelligent trade-offs there but
CHUCK: Yeah. And that’s the other thing, is it’s one of those things just like everything else where you figure out, “Oh that really hurt when I did that.” You figure out where the potholes are and you make mistakes and that’s how you gain experience in doing it right. The other thing is that with a good REST API, if you do deviate you need to make sure that you are absolutely documenting where you deviated at so that people know what’s different from what they expect.
JOE: So, here’s a monkey I want to throw. Sorry, go ahead AJ.
AJ: I feel like what I said got misinterpreted because I didn’t understand what… anyway, what I’m saying is a RESTful API is pretty much guessable, right? You more or less know. You’re like once you see the application, if users is with pluralized with an -s and then to get a new user you go to user/new, I pretty much expect that every entity in the application is going to be something like that. It’s this idea of following this CRUD, create, read, update, destroy pattern that within the application is consistent.
AARON: I’ve seen REST endpoints where all the actions and all the bits that need to be twiddled are in the URL and I’ve seen REST endpoints where it’s fairly shallow as far as the URL you’re doing some verb to but the action and most of the specifics are contained in the request like the JSON that you send in the post.
JOE: Oh, really?
AARON: Yeah. So, I’m not sure which one I prefer most. When I dorked around, and this was a few years ago and I don’t know if it’s changed, but when I was dorking around with the Facebook API, most of the stuff that how you affected what you were going to get back was in the request body in a JSON object as opposed to you organizing it into this big long URL.
AJ: So, you’re saying you could update /users and include the bookmarks array in that instead of updating /users and then separately updating /bookmarks/id.
AARON: Yeah, or maybe you just, you know I’m not saying it’s designed right but maybe you PUT to the users. And I know we’re using hypothetical endpoints here, but you PUT to the users and you have some action parameter in that object that describes what it was going to do. And that action is in the JSON object verses up in the URL. I’ve seen both and I don’t know which one’s right. Like when you guys try to define what’s REST, I was trying to get the answer out of that. Which one’s the right way and which one’s the wrong way?
JOE: There’s another third option in there and that is query parameters as well.
AARON: Exactly. I forgot to mention that, yeah.
CHUCK: Say that again. What parameters?
JOE: Query parameters, right? So if I do a GET to /user/1 or /users?id=1 are they both RESTful and which one’s right? Is one of them wrong? I think most people prefer, I don’t know, it seems to me like the industry as a whole prefers just URLs to describe everything which I think is shallow, weak.
JAMISON: In my mind, query parameters are for filtering. They’re for doing queries on the data set that you’ve returned.
JOE: Right, yeah.
JAMISON: So, if you require query parameters or a call will fail, I think that’s a mistake. But I think it should be changing the data representation that you get by hitting that main resource, not adding things to it.
AJ: Yeah, like a limit or a sort, something like that.
JOE: I probably don’t feel as strongly about it but I do like what you say.
CHUCK: Right. Basically where I’m at is that again, so putting the parameters into the URL itself basically says this is the resource. So it could be a list of users or whatever, and then the query parameters just refine that.
JOE: Right. What Aaron was talking about was putting information into the JSON. That gives you even more capabilities than you get out of the query parameters and how is that any different, really?
AJ: So, that doesn’t sound RESTful to me if you’re putting stuff into a…
JOE: A JSON package.
AJ: Yeah, if you’re posting instructions in JSON, that sounds more like RPC than rest.
AARON: Yeah, I don’t disagree. That’s why I was asking. I had seen people have a REST endpoint that was built like that and I was wondering if it was a good idea or not.
JOE: Well, it certainly gives you some versatility.
AARON: I will however say with regards to what Jamison was saying about the query strings. I do think they’re only for GETs, right? You shouldn’t be doing query parameters on a POST or any other verb there. It would only be on a GET, right?
JAMISON: Yeah. I think it’s [inaudible], yeah.
CHUCK: Yeah, but then you could put your query parameters in the POST header like you do when you’re submitting a form. So I want to move on to some other areas. We’ve talked…
JOE: Okay, I have another area I want to move to.
JOE: OData, vastly superior to REST. Go.
JAMISON: I don’t even know what that means.
JOE: Oh, you don’t?
JOE: OData’s the shiznit.
AARON: I got nothing.
JAMISON: I assume you’re correct then.
JOE: I don’t have an opinion between OData and REST. But OData is basically like, you know REST is the CRUD operations but you’re limited in what you can really say as you make requests? Like if you want to GET an entity, you get that entity but you can’t say, “Oh also while you’re at it, get me these related entities. And I only want these properties of the first entity and these other properties of the second entity. So just get me those and get them all in one request,” where OData is basically like SQL for an API.
CHUCK: Sounds like overkill to me.
JOE: Oh, no. Well, it’s so awesome because think about what Jamison was saying about performance issues, right? If you’ve got an endpoint that returns all the data for something and you just want one piece of data but you need it for everything and you want to limit just one property, that could be really difficult in REST unless you start building your REST endpoint to handle all that, where OData just has it built in for you. Windows’ got an implementation. Java’s got an implementation for it. I’m not sure what else. But it’s way cool. Check it out when you get a minute,
AARON: Yeah, it sounds wicked interesting.
JOE: It is. Breeze.js has an OData compliant implementation so you can hook up to OData servers.
JAMISON: So I have two questions about other things related to building apps on top of JSON APIs. One is if you’re doing a client side app, so you just get data from the server, how do you handle authentication?
JOE: So, the standard method is to at some point, the client side has to authenticate. I guess this depends on how you expect your API to be used. If you expect it to be extremely stateless, like every request might come from just one authenticated client and they might not make any other requests at all, then you make every request require all the authentication. Whereas the other way
JAMISON: Like a signing thing or something like that.
JOE: Right, right.
JAMISON: That’s how the AWS API works. You get a token and you sign your request with it and send down the signature and they verify it on their end.
JOE: And then you just use that. Wait a second. So, you use that token. There’s no sessions, right?
JAMISON: So there’s no [inaudible] request you make. Yeah, there’s no session. There’s some request you make to get, so when you sign up you get a private and public key. So it’s like [inaudible] key encryption, sort of, -ish. And you take the whole request you’re going to make and you hash it together somehow with this public key. And then you append the hash to the request. And then on the server they verify it with your public key.
JAMISON: Well, yeah. The basic result is that it’s sessionless because you send all the authentication information with every request.
JOE: Whereas the alternative is session-based which doesn’t mean stateful. It just means session-based. But you send in your authentication, you get some kind of a token back, and then you have to send that token with every request at that point to prove that you are the person who initially authenticated. And using some hashing, they can make sure, the technology makes sure that nobody else grabbed your token and tried to hijack your session.
CHUCK: Yeah. If you’re on the browser, then your browser will more or less manage most of the session stuff, right?
JOE: Yeah, just because you put the token in the cookie. So the very first thing that comes in is your middleware server will first check the authentication by checking the token. That’s hopefully invisible to you, right? And then at that point, you can say, “Alright go ahead. I know that they’re authenticated.” Or they populate the authentication information then inside the request. You make some checks and say, “Alright well is this person authenticated? Or do they have the authorization that I want?"
CHUCK: Right. But in the case of iPhone or some other server side application, it doesn’t necessarily handle or maintain cookies or session state, then yeah you have to do something like that where you’re passing the authentication information every time.
JOE: Right. So, what do you think, Jamison? Is that [inaudible] cover it?
JAMISON: That sounds legit. Yes. I think it is. What about if you’re doing more real-time-y things together with normal HTTP requests? Does that question even make sense?
JAMISON: So, say part of your app is streaming data, part of your app is on-demand getting it from the server.
AJ: Twitter has a TCP stream for their streaming updates. HTTP 2.0 is supposed to have good stuff where you can just give data as it’s needed.
JAMISON: So yeah, but right now when I’ve done it, it’s always felt really weird to me because there’s part of my app where everything is all RESTful. There’s URLs for it and whatever. It’s standard and it makes sense. And then all of a sudden, if you get to this special magic place in the code, everything happens over web sockets and you have no more URLs and you magically get data. I don’t know. It feels like there’s a disconnect there and I don’t know the best way to resolve it.
AJ: Well the stuff that’s going over web sockets, isn’t that live?
JOE: It doesn’t have to be. You can open up a web socket and start authenticating your users and making updates to your data.
AJ: Right, but what I’m saying is the use case for a web socket is generally something that is living. Like if I have a blog post then I have a URL to that blog post and that makes sense, or if I have a contact object in my app, I have a URL to that contact object and that makes sense. But I don’t necessarily, the web socket is the shim of the URL of what’s happening right now with a series of objects like status updates.
JOE: Well, you’re kind of implying that it’s only one way coming down to the client, but the client can also real-time be sending data back up to the client too, right? So maybe it’s fair that your application, it might make sense that, “Oh I want to get these blog posts. I might open up a web socket and start streaming the blog posts across,” because it just makes sense in the performance requirements or the behaviour requirements of the application, right? That’s possible.
AJ: Well, it seems like you just get an array of blog posts if you had them. Web sockets seem like real-time.
AARON: So additionally, web sockets don’t necessarily, you don’t translate into a URL syntax. But it has the notion of channels. So if you had different buckets of socket data you needed to pipe or listen to, the way they handle it is by subscribing to different channels or joining different channels and the server can begin broadcasting on different channels. So from what I’ve seen, it’s either via channel or when you send your message to the server, you’ve got some sort of a type in there and you have to switch on the type to find out what kind of a thing it was which, it’s all crappy right? But I’ve seen that happen, too.
JOE: That’s a good point. Let’s just say that you are listening for server updates about a very RESTful piece of data, like I want to know when somebody posts a comment to my blog and I want it over a web socket. So I get a push notification. That’s a very RESTful piece of data, right?
AJ: Well, I think that data, if you have a URL to it, that is a RESTful piece of data. But the live stream isn’t RESTful. It’s just a live stream.
AJ: There’s no state. I guess that’s the thing is a web socket is somewhat, you’re not transferring a state because it’s just whatever happens to be right now. I don’t know. I guess you could have a RESTful resource called /now but you’d have to say five things that are happening right now
AJ: Or you’d always get back an empty array because nothing’s happening that millisecond.
JAMISON: Right. The way we…
AARON: Yeah, they’re kind of…
JAMISON: I was just going to say the way we do it right now that still feels weird is for anything that has live updates, there’s also just an endpoint to get the entire state of that collection. So you can say give me all of the posts right now but there’s also a separate place to subscribe to a web socket for those posts, if we’re talking about posts.
JOE: RESTfully, probably…
JAMISON: But they have similar URLs and they, well not really, but you know what I’m talking about. It looks sort of similar and there’s a web socket analogue to the REST URL.
JOE: So, that’s interesting but I think that Aaron was the one who might have said the most useful piece of information. That is if you’re designing an API for real-time communication, what’s the best way to design that?
AJ: Rephrase the question?
JOE: You’re designing an API for real-time communication. What are the best ways to design that?
CHUCK: Doesn’t it depend on the data and what’s really important?
JOE: Sure. So does everything. So…
CHUCK: I’m giving my consultant answer again.
JOE: Everything depends.
CHUCK: It depends.
AJ: When HTTP 2.0 comes out in full force, we’ll use HTTP again [chuckles].
CHUCK: We did a whole episode on HTTP 2.0 on Ruby Rogues, just so you know. We’ll put a link to the show notes to that.
JOE: So Aaron, do you have any insights or wisdom about that?
AARON: [Chuckles] No. I know for live streaming data, I know what I have done and I don’t know if it’s kosher and I don’t know if it’s what other people have done. But I’ve done some channelling when I need different bits to subscribe to other bits. I’ll make a channel for one and I’ll make a channel for the other. Without using channels, your code gets so verbose on the server where you’re doing so much boilerplate switching on what people do and don’t want to listen to. And you’ve got so many maps recording who want to hear what. You have to use channels throughout the Socket.IO library to manage who wants to hear what. I don’t really have any great information. I only know what I’ve done. I’ve used channels and then inside of a channel I throw the query parameters, if you will, into the message body as properties inside that. So yeah, I don’t know if that’s good or bad. That’s just how I’ve done it. It’s worked. But I’ve never done anything humongous.
JOE: Right. Jamison?
JOE: Your turn. Your turn to bestow some wisdom on us.
JAMISON: I have no answers. I have no wisdom. I came to listen to you guys to gain wisdom.
JOE: You sound like you’ve been doing a significant amount of real-time over there on i.TV.
JAMISON: No. I don’t know. We do a little bit, but I’m pretty sure Aaron does more. It’s just what I said. I should be more specific. We have channels that match the RESTful endpoints. So if you want real-time updates, you subscribe to a channel. And it looks a lot like the URL to get the entire state of that resource.
CHUCK: So, is it…
JAMISON: So we don’t have lots of things. Oh, go ahead.
CHUCK: Is it polling?
JAMISON: No, no, no. You don’t poll. So when you first, the way our app works is it’s a real-time TV thing. So when you first go into the track for TV, you get everything that’s happened for that TV show so far up to now. And then you would subscribe to the channel for the updates for now. So we combine them both. But I have no idea if that’s [inaudible]. If someone can think of a better way to do it, I would love to hear it. Because I’m just stumbling around in the dark here.
AARON: So you’re like, you name your channels. The name of the channel is some URL, is that what you’re saying?
JAMISON: It’s not exactly the same. But it’s pretty close, yeah.
JAMISON: So you can pair them up easily. They actually go together.
AARON: Yeah, that makes good sense.
JAMISON: I think we have a lot different requirements than you guys do, Aaron.
AJ: So what about just having the resource be something like /news-items query sense equals UNIX timestamp. And then…
JAMISON: Then you have to poll.
JAMISON: The whole point of web sockets, well not the whole part. Part of the point of web sockets is to avoid polling. Because if nothing’s changed, you don’t want to send requests to the server.
AJ: Well that’s what I was saying is if you didn’t want to use web sockets. I’d still, I guess web sockets are mature now because you guys are using them.
CHUCK: [Laughs] That’s the definition, right?
AARON: So I prescribe what my users will use, what they’ll come to the show using. And when they’re coming in Chrome dude, there’s no reason not to use WebSockets because righting a backend with WebSockets is so, I think is real easy and your code’s real clean.
AJ: Do WebSockets work on mobile now? Do they finally sort that out between the 17 different versions of WebSockets?
JAMISON: So you can use the bare WebSockets. Most people that I know, I feel use a compatibility thing. Socket.IO there’s also third-party libraries like [inaudible].
AJ: I still hear people complaining that Socket.IO doesn’t work very well. And that’s what I was hearing two years ago.
JAMISON: There’s one called…
JAMISON: Is it Express.io? There’s another one that is the successor to Socket.IO that is supposedly better.
CHUCK: I’m going to lean in and shove us off of this topic a little bit because I have one other thing that I want to talk about and that is actually formatting the JSON. I’ve seen holy wars over this and [formatting], yeah. You must know some of my relatives in Spanish [inaudible].
CHUCK: Anyway, so the formatting issue. For example, Rails by default actually gives you an object and it has one key and that key is the type. And then it has the values, the actual rendering of the object. So then it actually has all the attributes on it. And you could turn that off, but I see people that are religious about, “Oh well now I know what it is,” and other people going, “Well I made the request to the resource and the resource tells me what it is.” So what’s your take on that? Should you tell people what they are or assume that they know or what?
AARON: So, I just kind of went through something like this at work and I didn’t think I had an opinion but it ended up that I did. So if I hit an endpoint and I ask, and the endpoint is things, right? I do a GET to things. I just want things to come back and be the things. I want them to be a collection of things. And they asked me, “Do you want it to just be a collection of things or do you want it to be an object that has a things property and that things property is the collection?” And so I had to think. I’m like, at first I didn’t mind but I don’t like that’s in an object with a things property inside of it. I decided I didn’t like that. But yeah, and we switched all of our stuff over to do the way I don’t like it. Because then when I come back
JAMISON: [Laughter] We know who won the argument, I guess.
AARON: Yeah, I lost. I mean, it’s because I’m the frontend guy. I’m not the backend guy. So now when I come back and I don’t want things anymore, I want foos, I don’t know. Now, the object comes back and the property for the collection isn’t things anymore, it’s now foos. I don’t know. I just like to have not so much meta crap in my response. I just want, for me, the collection. But I felt like some of the backend purists, they love to have this object come back with a meta property in it. It’s got an underscore metadata with all sorts of stuff that we don’t use inside it.
AARON: And then, sorry, go ahead.
JAMISON: Oh no, I keep cutting you off.
AARON: And then, you got your foo property which is the only thing you really care about. So I just thought it was weird to bury it one level deeper inside of the response.
JAMISON: I think it’s geared toward people that make tools to consume them automatically instead of consuming each resource specifically. That’s what the default REST adapter in Ember Data expects. If you’re getting /posts, you get an object that has a post property that has the array that’s actually your data. And I think it just makes it easier to wire stuff up automatically. But if you’re just writing the code yourself to do it, yeah it’s like, “Now I have to write .posts to get the actual data.” It’s just like one more line of code.
AJ: I think it’s weird to call the key what the resource is. To me, it makes sense to have maybe a value or collection or some generic type name like that and then have the collection be attached to that.
JAMISON: But then it’s not telling you anything. You already know you’re getting a collection.
AJ: But you can put some…
JAMISON: It seems worse.
AJ: additional meta information, like a warning or something like that, that maybe you also want to serve non-HTTP clients for iOS. I don’t know. I guess you’d want to do HTTP there. But having a little bit of space for metadata I think is a nice idea.
CHUCK: I think it depends on your use case. So in some cases, yes I agree. It’s nice to be able to say, “Hey this is what happened and here’s the data.” And in other cases, it doesn’t make a whole lot of sense. I think giving a status like success or not I think is a little bit silly if you’re going over HTTP because that’s what your HTTP return codes are for. And then you can still put stuff usually in the body to get more information if you need to. But at the same time, one use case that I can think of is for example, if let’s say that I’m doing some single-page app with a blog and so I want the posts and I want the comments. And I want users and so the users could be the author of the post or it could be the commenter or it could be something else in the layout on the blog. And so in that case, I could see making a request to posts and then getting a couple of collections that are all relevant to what I need to display on the page. But other than that, and then I get it all in one request instead of making other requests to get the comments and the users.
AJ: And I think that’s cool.
CHUCK: But it really just depends I think on your approach and what your trade-offs are. And if you don’t really have a strong reason for doing a lot of this stuff, I’m all in favour of keeping it simple. So in that case, I really do like just returning the stupid array full of simple objects and you just know that all the objects in there are posts because it’s all simple and you can make assumptions and it’s easy. But then if you find that you are DDOS-ing your server and you can save yourself enough work by combining this stuff, then yeah, go the other way.
AJ: So one question that I do have there is let’s say that you want to convey additional information about an error. Like you go to make a request, you can’t get back the array of items, so what you would get back is just an empty array. But you’d rather give back some information about the error. Then at that point, where do you put that? Do you put it in the HTTP header? Do you put it in a cookie? What do you do to get at it?
CHUCK: There are a couple of options. And again, this comes back…
AARON: Depends on the error.
CHUCK: to what’s important. Yeah, it depends on the error. It depends on what’s important. It depends on what you’re trying to convey with that error. It depends on whether the frontend even cares. It may just care, “Oh something bad happened.” And so you handle all those things differently. So you could handle things where you get the error response, a 403 or a 400 or something. And you just handle it and you just pull the error data out of the body of the response or out of the header. Or if it’s more convenient for you, then yeah, return a 200 error or a 200 response code and actually give them an error status in the object. I think that really just boils down to what you want to do and what makes life easier on you. What were you going to say Aaron?
AARON: I was going to say some of the stuff you said, but said more so good job man.
AARON: Good job!
JAMISON: We did it.
AARON: We did it.
CHUCK: So are there other formatting issues that we need to consider? One of the other things I see sometimes is that the keys on a JSON object are wrapped in quotes and sometimes they’re not. Does that matter?
AJ: It’s JSON. They’re wrapped in quotes.
CHUCK: What about the keys? Or the values, sorry. Like numeric values, should they all be strings or can they be numeric values?
AARON: They can be numeric.
AJ: They should be numeric if they’re numeric and they should be strings if they’re strings.
AARON: Booleans are Booleans. They’re just the word true or false obviously. Well undefined, do you have to put the word undefined in it. You just leave it blank or do you have to put the word undefined?
AJ: You can’t put undefined. Undefined isn’t part of JSON.
AARON: Oh that’s true. It’s part of [inaudible].
AJ: Yeah. You have to do either null or leave it out. I think null is kind of nice. One thing, if you’re having not a web browser consume stuff, what I found is that you want it to be as consistent as possible because as soon as you throw it into another programming language, especially if it’s typed like Java, the library is going to hate you so bad if everything about your JSON structure isn’t consistent. So never have something that’s sometimes an array and sometimes an object or sometimes null and sometimes a string. If it’s going to be a string and you think that you might have a Java client consuming it, then make it an empty string as the null value or try to keep the keys there and try to keep things of the same type if at all possible. Because libraries are stupid and they don’t do the right thing a lot of the time.
CHUCK: Wow, we’ve really been all over the place with this episode.
AARON: That’s pretty bold, AJ.
AJ: Well, no. I had, at SpotterRF I created a lot of example code for customers. And I’d be like, “Why is this so hard? It should be really simple.” But then I’d go to do it in their language and I do it in Java or .NET or C or C++ or whatever it was and try to use the library that they were using. And I’d be like, “Wow this library is ridiculous. Why isn’t it seeing oh, this is null so therefore it should just be an empty string if it has to type it.”
AJ: If you’re never going to do it, then fine. But in all likelihood, if you have an API, someone’s going to consume it and they’re going to consume it with a language that is statically typed and can’t understand the difference between zero and null very well or a property being absent.
JAMISON: I feel like there’s a veiled criticism in there and I didn’t understand it.
AARON: Yeah, it went over my head.
JAMISON: Chuck’s making fun of somebody and I don’t know if it’s me or not.
CHUCK: Yeah [laughs]
JOE: Yeah, explain it please.
CHUCK: I was just trying to say that the problem might be the language.
JOE: Oh, yeah. Yeah, definitely.
CHUCK: No, right tool, right job, right?
JOE: If any government is going to consume your data, pretend like their parser is a C parser because it’s going to be at least that bad or worse.
JAMISON: Well I just know how many times I’ve sent out object object as the value of my JSON key, so I can’t make fun of people for parsing stuff out too much. You know, when you’re trying to string an object [inaudible].
AARON: Yeah, yeah, yeah, yeah, yeah, yeah.
JAMISON: I’m guilty.
JOE: That’s just a, what would you call that, polymorphism at its finest.
JOE: Because everything’s contained in that one little string.
JAMISON: It’s true.
AARON: You just have to figure out what it means.
CHUCK: Yeah. Alright, well anything else that we should cover in here?
JOE: I’d like to come back full circle and ask each of you guys one question, okay? You’re building a new app from scratch. You’re the designer. You’re designing everything. You’re building your API and it’s got whatever kind of frontend you feel like it’s going to have. Do you build a RESTful API or a very strictly RESTful API or not? Aaron, go first.
AARON: Yes, question mark?
AARON: No, of course I do. When I hear the Rails guys say how easy it is to build Rails apps and I think building a client side, a thick client in the web browser with an Express, Node Express backend, I think that’s about as easy as it gets having never used Rails. So yeah, and when I use Node everything I do is REST. So I don’t even have another go-to option. It’s always solid REST. It returns 401s, 402s. I don’t 404 it. But I could. And everything, POSTs, GETs, PUTs, everything matters, the verbs. So I definitely don’t go by the JSONAPI.org stuff because preference. I could be convinced to do [inaudible] that. But yeah, that’s my take.
JOE: Alright. Explain the 404 thing you said.
JAMISON: Yeah, you use 401s instead of 404s? I got to go Google what 401 is. I don’t even…
AARON: No. On my own stuff, my personal stuff, not work. I just [inaudible] empty arrays because I don’t like to catch 404s because I’m lazy.
JAMISON: You’re the enemy.
AARON: I am, yeah.
JOE: Alright, Jamison. You’re next, Jamison, go.
JAMISON: [Chuckles] 401’s unauthorized, dude. I was like, “Wait up.”
CHUCK: 403’s unauthorized.
JOE: So is 401.
CHUCK: Oh, is it?
JAMISON: 403’s forbidden.
AJ: I thought 403 was payment required.
CHUCK: No, 403’s forbidden. You’re right. My bad.
JAMISON: Man, we’re such nerds.
JOE: Okay Jamison, go.
AARON: Our cue to give up.
JAMISON: Yes. It’s the default, unless there’s a reason not to. Then I would just do it, because I think it makes sense. So yes until there’s a reason not to. But the reason not to isn’t that I didn’t want to. It’s like, “This is way too slow to do with REST,” or, “This is real-time and it doesn’t make sense,” or something.
JOE: Alright. AJ, you’re up.
AJ: I’m less sure of what REST is after our conversation. So, I don’t know anymore.
AARON: [Chuckles] Sorry, users.
AARON: You picked the wrong week.
CHUCK: I am Tron. I fight for the users.
JOE: What I know is that I always return 418 no matter what.
JOE: But I think that just being consistent, like having the idea of that CRUD interface is good. But I don’t think that you necessarily need to be extremely, extremely, extremely strict about it because sometimes HTTP just doesn’t do stuff well. Like sometimes you have to POST when what you want to do is a GET because GET doesn’t have a body and query parameters just don’t serialize well for certain types of things that you need to do. And sometimes, you want to do stuff like I don’t know if it’s technically RESTful what Chuck was saying earlier, where you get the posts and the comments and the users all at once and then on the client side reconstruct it to match each user to the post or the comment or whatever. But I think that makes a lot of sense and it sounds RESTful to me.
JOE: But it’s not strictly RESTful in that I make one request per user and one request per comment and one request per blog article, which I think would be really weird. Okay, Chuckie, you’re up.
CHUCK: [Chuckles] I just heard AJ, and I’m sitting here thinking, “He’s just trying to justify being a REST hippie.”
AJ: I don’t even know what means, but I agree.
CHUCK: So yeah, I tend to follow the REST convention as defined by Rails, because that’s usually what I’m working in to build these APIs. And yeah, if I have a reason to break with the standard PUT, POST, GET, DELETE convention, I’ll do it. But usually it’s out of major performance or major convenience wins. Otherwise, I just stick to what they’ve got there. So that’s my approach.
JOE: So speaking of 400 errors, I recently had a reconstructive surgery on my knee because my ligament was torn. And Aaron, do you remember who made the joke?
AARON: Me. [Chuckles] yeah.
JOE: Yeah, it was Aaron. So Aaron, tell them your joke. Let me explain it. My ACL was actually just completely missing. The doctor went in for some other purpose and I had absolutely no ACL. So I just had a surgery to [inaudible] this. I was telling this to Aaron and Aaron says…
AARON: Dude, you 404’d your ACL, bro.
JOE: [Laughs] There’s a geek joke for you.
AARON: That’s geek.
JOE: That’s geek right there.
CHUCK: So Joe, if you were building an API would you go with REST?
JOE: You know what? I’m probably going to vary just a little bit and say I probably will go kind of half. I think I feel like I do more REST when it makes sense and do more like an RPC type feel when I just feel like, “Oh I want an operation that does this and call it over the web.” So I’m a little bit more loosey-goosey than the four of yous.
AJ: So an idea of something that’s not very RESTful. Phone calls. Text messages with Twilio. That’s something where it makes a lot more sense to do RPC style stuff because it just doesn’t really fit into the paradigm, right? If you use their API, you can understand what I’m saying, maybe.
CHUCK: But you’re not dealing with a RESTful resource per se there. You send it off.
AARON: I disagree, bro. I think Twilio is REST, man.
AJ: Depends on which part you’re talking about. Because one part is super RESTful, but then the other part of their API is really, just because it’s so stateful, it’s entirely stateful in terms of sessionfull is what I mean.
AARON: Hmm. Let’s chat about that after because I haven’t seen that. I’ve done some Twilio and I thought I love how you REST into their system and they REST the stuff back out to your system. It’s REST in, REST out. I think they’re pretty solid.
CHUCK: It says REST on the API docs.
AJ: Yeah, there’s a REST API…
JAMISON: They’ve been split in two.
AJ: But then there’s the other API.
JOE: Have we had an episode on Twilio? Maybe we need an episode on Twilio.
AJ: We should. It’s amazing.
CHUCK: No. We need to do that.
AJ: I love Twilio.
AARON: Yeah, we should definitely do Twilio.
JOE: You can get us a Twilio dude on here?
AARON: Yeah, definitely.
JOE: Awesome. I’ll shoot you an email about it.
AARON: He was in Florida with me last week. So yeah, definitely.
JOE: I’ll shoot you an email and cc Mandy to start it.
CHUCK: Alright, cool. Alright, well we have been talking about this for an hour and seven minutes and we [inaudible] the picks.
JOE: Holy cow.
CHUCK: So, we’ll do the picks. And we’ll wait to see the comments where it’s like, guys where do I even start?
CHUCK: Because there are so many opinions on this. I’m really curious to see what people think. So, please go leave a comment. Please.
CHUCK: Alright, Aaron.
AARON: Alright. So, I’m going to go with picks. Number one, I got a Sphero. Have you guys used a Sphero?
JAMISON: Oh yeah.
CHUCK: Those are those little programmable balls with the [inaudible] lights?
CHUCK: I’ve seen them.
JAMISON: They roll around and stuff.
AARON: Yeah. So, I bought one and it’s pretty friggin’ awesome. So, I fell in love with it. It’s fun to build apps for it.
So, my second pick is going to be CylonJS which is a pretty awesome lib to talk to these things. I love them so much that we actually, we’re going to do a Sphero hackathon at ng-conf in January. Actually, the author of Cylon’s going to come out. So, I’m going to pick Sphero. I’m going to pick CylonJS. That’s my picks.
CHUCK: Awesome. Joe, what are your picks?
JOE: Alright. I’m going to have two picks. I’m going to pick the board game Mascarade, spelled M-A-S-C-A-R-A-D-E so it’s a funny spelling, which matters because there are board games that are spelled with the normal spelling.
JAMISON: I can’t spell that word anyway. So, it’s probably how I would spell it.
JOE: It’s a really, really enjoyable game. And I think I’ve picked this before but I just really enjoyed it so much I want to pick it again. It’s a party game. You can play it with anywhere from 3 or I think 4 up to 14 people. Plays in 20 minutes and it’s a bluffing type game and just really, really, really fun. So, I’m going to pick Mascarade.
And then my other pick is going to be the pair of books ‘The Winds of War’ and ‘War and Remembrance’. They were authored, I don’t know, in the 80s or something, sort of a historical romance about World War II, although romance is kind of stretching it because it’s just more about this family of people during World War II. And just an amazing set of books, if you like World War II or interested in World War II at all. It’s just an absolutely amazing set of books, beautiful books. I’m in the middle of the second book, ‘War and Remembrance’ right now. And absolutely one of the best pairs of books ever written, especially historical fiction.
CHUCK: Awesome. Jamison, what are your picks?
JAMISON: I have two. One is Gatling, which is a tool for doing load testing. I was looking into this for a couple of weeks and this is the best open source load testing tool I found if you need to do flexible. We’re trying to recreate user actions to an API but also need the flexibility to fill in dynamic values. So say I want this user to go to this page, if you want to hit this specific resource but with a range of possible IDs or something. So it’s been really great and it’s pretty impressive how much load you can do off just one machine.
Then my other pick is a website called ChillestMonkey.com and it’s a picture of a really chill monkey. That’s it.
CHUCK: Awesome. AJ, what are your picks?
AJ: First of all, I’m going to take a look at this picture.
AARON: It’s awesome.
JAMISON: It’s true. That is the chillest monkey.
AARON: I love that guy.
AJ: That is definitely the, he basically puts the Lion King to shame because he is king of the jungle. He’s that chill.
CHUCK: I was so expecting to get rickrolled right there.
JAMISON: Oh, no.
JAMISON: Next week.
AJ: Okay. So technically, I will pick PassportJS and OAuth2orize. I must say that that guy, Jared Hanson, is a stud because he just cranks out all sorts of module that are auth related and they seem to be pretty good and easy to use. And he’s got documentation and stuff. It’s not like it’s the perfect documentation or the perfect libraries but they’re really good. And he obviously has a handle on how all that stuff works and puts it all together. So, props to him and his libraries that make doing hard things easy.
I’m also going to pick my roommate’s fish tank because Chuck loves the sound that it makes on the podcast.
And I’ll pick, there’s a couple of books that I think we’ve picked before and I just want to re-pick them because they’re cool. ‘Influencer: The Power to Change Anything’ and then ‘Nonviolent Communication’. I think at the core of both those books, one of the core principles in both of them is the idea that you are the master at the helm. You are the captain of your own destiny. And so you have to take personal responsibility for whatever is going on in your life. Because until you take personal responsibility, you will create undesired outcomes in your life. Like if I say, “I have to go to work,” for example. Well I don’t have to go to work. I choose to go to work because I want to please clients or because I want to make money or because I want to provide for myself and my family. Whatever it is that you want to do it for.
AARON: Have food to eat.
AJ: Right. And so, both of them do go into that kind of realm of self-responsibility which I think is really cool.
And then lastly, I’m just going to pick my little project site that I’m working on, DateProvo.com, because it’s got cool ideas on it if you’re into dates that don’t involve alcohol or really, basically fun teenager-type dates. That’s what’s going up on there. And some other people are submitting stuff and it’s cool.
CHUCK: Awesome. Alright, so I’ve got a couple of picks. The first pick I’m going to pick is Fiverr.com. I had a little logo done for Going Rogue, which is the video series that I’m working on. It was pretty good and fairly inexpensive. So, both are things that I like.
I’m also going to pick Hover again. I just love their stuff. It is so awesome. They are easily the best domain registrar that I’ve ever used.
And finally, I’ve been a fan for the Shark Tank show for a while, the TV show. And they put out a book. And I’ve been listening to the book and it’s pretty basic business stuff. But if you’re looking to start a business, it was worth it for me to listen through. I’ve been listening to it on Audible. It’s been worth it for me to listen through and just get some of the basics back in my head and go, “Oh I need to do this better. Oh I need to do that. I need to think about this other thing.” And so anyway, really enjoying that. So, those are my picks.
And I guess that’s it. We’ll wrap the show. We’ll catch you all next week.