Build Your Own Content Hub With GraphQL
Data has been decentralized through APIs, SaaS, & other platforms. Pulling it all together in a developer-friendly way can be tricky. Eve Porcello will teach us to tame our data using GraphQL!
Links & Resources
Click to expand the full transcript
Captions provided by White Coat Captioning (https://whitecoatcaptioning.com/). Communication Access Realtime Translation (CART) is provided in order to facilitate communication accessibility and may not be a totally verbatim record of the proceedings.
JASON LENGSTORF: Hello, everybody. And, welcome to another episode of Learn With Jason. Today, on the show, we have Eve Porcello, one of my favorite people in the tech community, one of the best teachers I've ever seen.
Eve, welcome to the show.
EVE PORCELLO: Thanks so much for having me. This is very exciting to be here.
JASON LENGSTORF: I've been looking forward to having you on a guest. I'm super, super excited. For those of you who aren't familiar with your work, want to give a little bit of background?
JASON LENGSTORF: Where's the book? Is it Learning GraphQL?
EVE PORCELLO: Yeah.
JASON LENGSTORF: I'm posting a link in the chat for all to see.
Like, you're also an active speaker on this stuff. I've seen you give some really amazing talks. You're an Egghead instructor. You're all over the place, teaching people to do really cool stuff in the GraphQL space.
So, I guess maybe the big question that I see, from a lot of people whenever I bring up GraphQL, is "why?" To make that a little more specific, what is the advantage that you get from using GraphQL, that you don't already have when you're using a system like REST or whatever else you're using?
EVE PORCELLO: Yeah. I think GraphQL the "why" of GraphQL, I'm not really here to force anyone to use GraphQL. I don't want to have you hate GraphQL and then I'm the person who represents what you don't like.
But I have found a lot of joy in GraphQL because of what it gives us, communicationwise, because GraphQL schema is a really nice way to bring everybody together, decide on what we want to build our app around. What are the types? What are the pieces of data that I actually need? It can become really tempting to have everybody go off and work on projects separately and I think the schema gives us a really nice to way of communicating what we're actually trying to build. It's a nice, sort of, projectplanning tool. And you can pull in your data anyway.
The reason you might use it over REST is not all data is easy to wrap with a REST API. We may have cloud services, all this data all over the place and GraphQL gives us this nice layer over it so that we can get our data from anywhere, really. So that's why I think it's cool. But again, make your own decision. (Laughter).
JASON LENGSTORF: I'm pretty onboard with that because I feel like the thing that really made GraphQL, like, the light go on for me with it, was when I was working at IBM and we had so many microservices and there was this ongoing discussion where you would plan a project and it was to figure out who owns this data and get a map of what data is available. And it was this really complicated process because we'd have to reverseengineer and figure out where it was hosted and learned which team was running it. Granted IBM's a huge company. Who owns that REST API? Send them a quick note and figure out what they're exposing.
With GraphQL, if all the data goes here, I can open this thing and open this docs tab and we can see all the data and I can rate here to see what data's available and click buttons until I see what I'm getting back and then I can write my app and that, in and of itself, just that one change cut weeks off of our project development times and I thought that was that was when I became a believer. I was like, okay, this is the way we can build frontend apps on teams without all of the pain. Without that feeling that, like, teams don't communicate. Because it does the communication for you, I love that you said that about the communication of GraphQL.
Um, yeah. So, quickly, thank you, Kaley, for the sub. Ice Cream Tango, I know the boops are broken. I'm in the process of building something that's really cool for streaming. I'm trying to put the wings on the plane as everything is in motion. You know, it's held together with, like, bubble gum and best intentions at this point. (Laughter). Speaking of bubble gum and best intentions, we're going to build something from scratch today. (Laughter). Eve, what do you want to build today?
EVE PORCELLO: Well, here's the thing, I was watching our president be interviewed and he was talking about this test that he took. It was sort of like a competency test. I don't know too much in detail about it, but the way that you
that you could say you could prove that you're understanding what's going on is you're able to retrieve data, specifically like a sequence of data. What he said was you're supposed to remember person, woman, man, camera, TV. And if you know that stuff, you're recalling everything correctly and you're proving that you understand what's going on.
So what I'm thinking is, as a teacher, I really want people to remember that they can get the data that they want from anywhere. So, I think GraphQL's a really great tool for remembering those things. So, maybe you can remember a person, woman, man, camera, TV. That's what we're going to build today, using GraphQL and Apollo server. (Laughter).
JASON LENGSTORF: I'm so ready. Make sure you go and follow Eve on Twitter for all sorts of funny content, just like we're going to build today.
Okay. Ryan, thank you so much for the sub. I apologize, everybody. The boops are broken today. I'm trying to figure out why, but I'm not going to get it done during this stream. Thank you for the [Indiscernible], Shawn, that's wonderful.
Also, we talked a little bit about Eve's book. Go and get a copy of that. And even Alex. Alex was on the show before, so we have an episode I need to get actual there it is. On Apollo Federation, which is more GraphQL goodness. Get in there, for sure. And, yeah, then let's see one more thing, let's talk a little bit about your sponsors. Today, the show is being livetranscribed by White Coat Captioning. We've got Netlify, Fauna, Sanity and Auth0 all kicking in to make this show more accessible to more people and we really appreciate that, so thank you so, so much.
With that, Eve, you sent me a repo that we're going to be using as a starting point here. And so this is the let's see...the raffle yesterday the Tuesday raffle, I should say. I will post those results in a minute. I've got to look that up. I will get to that by the end of the stream, but I got to like, actually find that. I don't know where it went? Dashboard? No.
Eve, do you want to talk a little bit about this repo and we can start digging into it, like, where I should start?
EVE PORCELLO: Sure. So, basically the repo has a start branch and a finish branch. If you want to jump ahead and spoil the ending, you can. We'll be starting with the "start." Basically what we're going to try to do today is try to build a GraphQL server that's going to pull data from multiple different sources. We have so many data sources where all our data lives. A lot of folks that are new to GraphQL, they think we have to use one type of database or wrap one REST endpoint. You can use GraphQL to pull data from anywhere and GraphQL becomes this awesome source of truth, location for knowledge that you can get all of your data in just one place.
So, yeah, I guess we can just close the repo at the "start" branch.
JASON LENGSTORF: And I did that, to get us up and running here. So, here is our repo. And we've got a liveshare going. So, Eve is in here, in case I get unruly and she needs to get me back in line.
Okay. So, let's take a look at what we've got so far. We have a README. We have Markdown files. We have index.js. Here's a person. And here are names of women.
EVE PORCELLO: Yes. Those are my friends. They don't know that they're in this. Don't tell them. (Laughter). So, basically, in the index file, that's where our Apollo server lives. It's kind of the starter for our project. Um, if we think about what we need to get started with an Apollo server, it's really just our schema and the resolvers, like, those functions that go get the data. So, that's where we're going to start.
JASON LENGSTORF: Nice. Okay. And then there's a little bit of feedback coming from your mic, like it's rubbing on something.
EVE PORCELLO: That's not cool. Be a professional. Oh, that's why. I'm not even mic. How's that?
JASON LENGSTORF: Oh, we just we are I don't even know. Like, if Guy Fieri was here, he'd say we just entered flavor town.
EVE PORCELLO: I've spoken into a microphone before, but it doesn't always go (Laughter). I should do better.
JASON LENGSTORF: No worries. No worries at all. No, I'm glad I said something because I didn't want to harass you about your mic and you be like, "I know, it's the only mic I have."
EVE PORCELLO: I have a real mic. Unprofessional. I'm so sorry. Get it together.
JASON LENGSTORF: No problem. We want to get into our initial query and resolvers. Apollo server is for serving Apollo or GraphQL. Okay. What is.end?
EVE PORCELLO: So, the Apollo server, you can use it to build a GraphQL API. .env is going to help us pipe in data variables. We're going to pipe into MongoDatabase. Anything that we don't really want to commit into a public repo is going to go in a.env file. You can create it now or create it in a second. That will just go in the root of the project.
JASON LENGSTORF: Yep. And it's already "get ignored." I have this cool thing installed where it'll mask so we can set this and whatever the value is doesn't show up so we can actually add the environment variables.
EVE PORCELLO: Oohhh. That's so cool.
JASON LENGSTORF: It's called Cloak. It's a VSCode extension.
EVE PORCELLO: For me, I show everyone that stuff and I actually check database details into public repos all the time. I just wrote that down so I use that properly. (Laughter).
JASON LENGSTORF: There's a sound effect people can play when I show secrets. (Laughter).
EVE PORCELLO: Awesome. Sorry.
JASON LENGSTORF: Hackers, you dirty hackers.
Yep, there it is.
EVE PORCELLO: Exactly. (Laughter).
JASON LENGSTORF: Okay. So, we have so, then we have our type definitions and this is the part that I really like about GraphQL because this that's the whole thing, right? Like, this is the thing that we're going to get. We can make a query. It's going to be called "welcome." And we get back a string. And, like, there's some stuff to know. You got to know what a query is. You got to know why this is here. And, why is that there?
EVE PORCELLO: Everyone in the audience should know. It's a question. I'm joking. (Laughter). It just means that it is nonnullable so it has to return a valuable, it can't return "null." That's what that exclamation point is for.
JASON LENGSTORF: It can return an empty string, but it can't return the actual value "null," which is important with data because I feel like there's a difference between "we didn't get anything" and it was explicitly set to empty.
EVE PORCELLO: Totally. And everything in GraphQL is null by default. We have to elect to add that exclamation mark to make it nonnullable. We could talk, for days, about nullability, in GraphQL, but we won't go too deep down that rabbit hole.
JASON LENGSTORF: Next, we've got resolvers. I see a query here. I see a query here. I see "welcome" here. I see "welcome" here.
JASON LENGSTORF: And, to set this thing up, here's our start script. So, we want a new server. We put in this type def objects we created up here. Tell it what port to use. Wow, okay. That makes sense to me. How do you feel, chat? Do you have any questions about what we've done, so far? While we're waiting for the chat to think and type, what should we do next?
EVE PORCELLO: So, next, now that have we started this yet?
JASON LENGSTORF: We haven't, so let me do that. I will run an NPM Install. I'm going to look up the start command. Start.
EVE PORCELLO: It is literally just "start."
EVE PORCELLO: Totally. And the playground you see running on Local Host 4,000, that comes on Apollo Server. There's a browser querysending tool that comes with every server. This is really nice if you want to host this locally for folks who aren't necessarily developers. If they need data I used to be a project manager and I didn't know too much about code, but I would send requests with postmen all day and say, I don't know what's in these APIs, there's no documentation anywhere. This is a nice interface for knowing exactly what's going on with the predictive text and the panel and the schema over there.
JASON LENGSTORF: You could know nothing. You don't even need to know the first letter. I can hit "option+space" and it shows me everything that's available and that is really cool stuff.
EVE PORCELLO: Totally. Yeah. We're not here to memorize stuff.
JASON LENGSTORF: Exactly. That's something that I've been learning, more and more, as I've gotten further in my career. My job isn't to remember things, my job is to know where to look for things. We make a joke that senior devs just know how to Google. To a certain extent, that's true. You're learning what questions to ask and where should I look? I can't remember anything off the top of my head but I do remember, sort of, I've seen that before, I should Google this and I find my answer.
But, okay. So, I'm excited. What do you want to do next?
EVE PORCELLO: Well, we say we don't have to remember anything, but we do want to remember person, woman, man, camera, TV. (Laughter). So let's start at the beginning and we're going to we're going to create a type in our schema. So, we'll start with "person," first. (Laughter). And actually, there's a little file called "person.png," which is how all productionready things look. This is very realworld. It is realworld data. We've got an ID, a username, an address, a birth date and an email.
JASON LENGSTORF: If I move this over here, then we can just start putting so, do I need to put an ID in?
EVE PORCELLO: You do. Put in "underscore ID."
JASON LENGSTORF: I'm going to put in field names.
EVE PORCELLO: Perfect.
JASON LENGSTORF: Then, how do we choose a type? This one's pretty obvious, it's a string. But what's an ID?
EVE PORCELLO: If you're returning just a single value, which all of these seem to be, there's five scaler types. There's an ID, a string, a boolean. So, yeah, we basically if we're not returning objects or arrays or more complex types, we're going to place those just after the colon there. So, we need to decide which of those belong where. Unfortunately, our data is (Laughter). It's not always going to have all five. I should have made this "return all five," but that's okay. But the first one is kind of a
JASON LENGSTORF: Seems reasonable. There's a question about Nikki is asking about the convention of the underscore?
EVE PORCELLO: That's a really good question that I should have explained. The database we're using, picture yourself you're trying to build a GraphQL server to wrap your actual database and we have it in a MongoDatabase. That's how they decided to name the ID. There's always an "underscore ID" field. So, that's just that's just a Mongo thing.
JASON LENGSTORF: Username looks like a string. I'm making all of these required. Should I make any of them nullable?
EVE PORCELLO: No, that's okay. We'll be really bossy with our people here.
JASON LENGSTORF: Birth date looks like a different type.
EVE PORCELLO: Let's just make that a string for now. We could make that a custom scaler, which is we would define the rules around what a date is. For the purposes of our person now, let's just keep that a string.
JASON LENGSTORF: What's up, Will? Good to see you.
So, this seems good to me. We've got a type. It matches up with the sample data we have here. Okay. So, then the next thing that we need to do because like, right now, if I save this, it's going to break, right? Because there's no it shows me a schema, but there's no, like nothing reaches it. I guess it doesn't break, it just doesn't do anything.
EVE PORCELLO: Yeah. So, um, yeah, basically because our people are coming from a database, the next step is we need to actually set up that connection to the database, inside of the server. And then from there, we can add our resolver, because that'll be that'll be the easy part.
JASON LENGSTORF: Okay. So, to start, I want to do what?
EVE PORCELLO: So, to start, if you take a look you don't have to, but in the package JSON, there's a MongoDB. We can import that. So the import is going to be called "MongoClient" and that'll require MongoDB and then ".MongoClient."
JASON LENGSTORF: Do I want it outside on its own, inside the start function?
EVE PORCELLO: You're going to want it inside that start function. That's an asynchronous function that's first going to make the connection to the database and then we'll start the server and then once we start I'll talk about that in a second. Don't listen to me. (Laughter). What we actually want to do, though, is right underneath yeah, on the next line, you'll add URI, "cons URI." And then we're going to pull that from our.m file. So it's going to be "process.env" we'll make it all caps MONGO, underscore, URI.
JASON LENGSTORF: That means I'm going to need one of those in here. MONGO URI.
EVE PORCELLO: Yeah. The "put DB info here" thing, I'll do that for you. I think I know where that is.
JASON LENGSTORF: I might pull this offscreen for one second so we don't leak secrets.
EVE PORCELLO: I don't really care if you want to. (Laughter). Here.
JASON LENGSTORF: Where did you put it?
EVE PORCELLO: I'm going to post it in the MD file. I just didn't want to break my working app, because it is posted online. I'll show you that in a second.
JASON LENGSTORF: Okay. So, we dropped that in. (Laughter). Apparently Cloak doesn't work when you extend beyond the oh, weird. It's because it's got extra parenthesis in it.
EVE PORCELLO: That's funny.
JASON LENGSTORF: We'll quote it. So now we've got an environment variable. So we've got our URI.
EVE PORCELLO: Uhhuh.
JASON LENGSTORF: And then, do I need to create, like, a DB?
EVE PORCELLO: You'll create the client next. You'll say "cons client" and then "await client.connect." And then pass the URI in there.
JASON LENGSTORF: Not in an object?
EVE PORCELLO: Not in an object. You'll create "cons DB and client equal
JASON LENGSTORF: Like this?
EVE PORCELLO: Yep.
JASON LENGSTORF: Okay.
EVE PORCELLO: So that's what we need to do to get started with database. So how do we make all of our server functions aware of that database connection? Well we need to pass it via context. So we're going to create another function here. You'll add context there.
JASON LENGSTORF: Okay.
EVE PORCELLO: And this is going to be let's see...it'll be another async function. We're going to create a "people" variable, so "cons people." We're looking up what collection, in Mongo, those people are. In the collection's called "customers."
JASON LENGSTORF: So, DB Collection, customers?
EVE PORCELLO: You got it.
JASON LENGSTORF: Okay.
EVE PORCELLO: And what we want to return here inside of that function is "People." It'll be an object with "people" inside of it.
JASON LENGSTORF: Oh, an object with "people" inside of it, I understand. (Laughter).
EVE PORCELLO: That sounds really weird.
You know what I mean, I think. Yikes. Yeah, that's what I meant, an object with "people" in it. A total normally thing to say.
We have our data connection hooked up. We have our context. What's next is we didn't write our resolvers, so let's go ahead and do that.
JASON LENGSTORF: There's a question from the Stone Cold Man about modularizing this. When we're writing out our types, how would you go about doing that? Is it one, giant schema file? Or do you break these up? How would you go about that?
EVE PORCELLO: I think at some point, you probably will want to break down your schema into smaller pieces. It's just natural that the schema, as it gets longer and longer, starts to become a little overwhelming. There's a link in the chat about modularizing it into different files. You could do it by type. There's a lot of different strategies for dealing with that. Ultimately, you don't want to start breaking things down until you need to. I usually like to start with things as one document and then if we get to a place where it's just too much to maintain or a bunch of people are working together and it's just, like, we have no clue what's going on here, then you can start to break it down.
JASON LENGSTORF: I feel like that's a good, just as a general programming truism. If you start by putting everything in one place, you don't have to open a million files. Resist that modularization. Otherwise, I feel like it sucks to be modular when everything's tiny. If I have a million files with eight lines of code each, I'm so upset. (Laughter).
EVE PORCELLO: Totally. 100%. If you're thinking about a schema as a communication tool across all different types of roles in your organization, if people are looking at things in different files, that's superstressful because you're going to miss stuff. Don't make it too complicated to start.
JASON LENGSTORF: Exactly. So, we've got our person type. We've got our people data in the context and so now we're ready to write a resolver. So where does one start with such a thing?
EVE PORCELLO: Where does one start?
(Laughter). Basically, I'm thinking we want some sort of a query for a person. So, I guess really we really we want to instead of "welcome," if we're doing things in an organized fashion which I fail at a lot we would create a person and we're going to return their person.
JASON LENGSTORF: Okay.
EVE PORCELLO: Let's also add some arguments to that person. So, like, right here...um, we'll say "ID."
JASON LENGSTORF: Okay.
EVE PORCELLO: Actually "username" is what we want to do because "ID "will be a nightmare. There we go. It's very hard not to type. Yeah, that's what we would do. So a GraphQL argument, think of that as being a filter that you can add so that you can just see the person we want by username.
JASON LENGSTORF: Okay. And so now that we've changed the query in the type definition, I want to change it in the resolver, so I change it to "person." Then do I just get that argument? Am I going to get "username" like this?
EVE PORCELLO: Um, yes and no. Every resolver has different arguments that are passed to it, that we can use for different things. What I always do, when I'm teaching, is I say the first argument I like to call it "parent." It's up to everyone in the chat to keep me accountable and to explain that. So I'll add a "todo" here. "Yell at Eve if she doesn't explain "parent." Okay. That's an old joke, but I will say it again.
We'll use that. Remember these resolver functions can retrieve data from anywhere. So, if I choose Mongo as my database, I will use Mongo functions. I'm going to pop an async here because we are going to we are going to be waiting, potentially, for that data for a second, as it loads.
JASON LENGSTORF: We can make our resolvers async?
EVE PORCELLO: Yes, you can.
JASON LENGSTORF: That's a pretty big powerup, I think.
EVE PORCELLO: Absolutely. Our data is in Atlas, which is Mongo's database cloud. I expect some latency in that process. If I was doing a fetch request, I would expect latency there. You can use the async await for your resolvers.
JASON LENGSTORF: We've got parent and then a second argument.
EVE PORCELLO: The second argument is "args." Any time a user is searching for a person by their username, we're going to access it in that "args" object. The third argument is going to be "context."
JASON LENGSTORF: This, here, is whatever gets passed in here?
EVE PORCELLO: Bigtime. Yes.
JASON LENGSTORF: Okay. Okay. That makes sense. If we make this into an actual function, I would be able to get "username=args username. And the third one is "context?"
EVE PORCELLO: Yeah. Context is this little container where I can put anything that our resolvers need to know. In this case, it's details, where to go look for that data. It could be authorization tokens, it could be yeah, just like anything your resolvers need to do lookups or anything like that.
JASON LENGSTORF: Here, I would be able to do, like, "people=context.people." So, this will always be the same in every resolver. The args will be different based on what like, what query was called so the args will match this and we'll talk about the parent in a minute. Are there any other arguments we need to talk about?
EVE PORCELLO: Not today. (Laughter). There's one more called "info." That's for dangerous circumstances where you want to do something wild with the schema. But we won't be walking that path today.
JASON LENGSTORF: Danger and doing something wild is my middle name. (Laughter). Is the context function executed every time a function a executed?
EVE PORCELLO: I believe so, yeah. Is that true?
JASON LENGSTORF: I don't know.
So, I think what what will happen, as you get deeper and deeper into GraphQL, is you'll find ways to do batching queries or functions or if it's always the same data, you can make sure it only executes once. There's a lot of stuff you can do. To get started, don't think about that too much. You'll know when you have a performance problem and until you have one, don't stress about it.
Okay. And then, one other question, from Nikki, here, if resolvers can be async, does that mean you have to handle the "promises async functions return?" I think GraphQL does that for us. We don't have to do a "resolver.bin"?
EVE PORCELLO: That, we'll take a look at, in a second, when we do the fetch. If we want to do data transformation or anything, we can. In the context of this one, no context pun intended that's not a pun.
We would just use this Mongo function and that's going to handle everything for us. More on that in a little bit, Nikki, because we can use those operators, which is sort of sweet if we like that sort of if we love chainin', you don't have to stop doing it in GraphQL.
Our person is right now returning let's see...so all we need to do now is we need to call let's see..."people.findone."
JASON LENGSTORF: Why can't I spell "people "today?
EVE PORCELLO: It's a weird one. Where does "find one" comes from? It comes from Mongo. It's part of that library.
JASON LENGSTORF: I'm going to link to the MongoDB package.
And, let's see, we're almost to that point. So, now we've got a person. Do I need to await this or anything?
EVE PORCELLO: Ummmmmm...yeah.
JASON LENGSTORF: Okay.
EVE PORCELLO: And then you'll just return the person.
JASON LENGSTORF: Person...okay. So, then if I am correct and we haven't typoed anything what will happen is I'll be able to put someone's username in as an argument and we will get back a whole person object. So we'll be able to get all of this? So, let's give this a shot. Do you know a username I can query for? Actually, let's just see what it does here. So I'm going to do my query and do the I got to reload the page. Okay. There's person. All right. And then if I do a parenthesis, it shows me username. Okay. And so, what username should I query for?
EVE PORCELLO: Charles Hudson, all lowercase.
JASON LENGSTORF: Okay. And then, in here, we can choose what we want to get back. And so, this let's start by just getting everything, I guess. Address. Here's birth date and an email and then if I run this, fingers crossed, drum roll. Beautiful. Look at that. That is gorgeous.
EVE PORCELLO: So lovely.
JASON LENGSTORF: If I needed their address, I already know their username, so I just need their address. Boom! That's all we got. Or I just need their email. Ah. Gorgeous. (Laughter). So good. I love this. Okay. So, we've got people. Person.
EVE PORCELLO: Person. (Laughter).
JASON LENGSTORF: So, what's next? "Woman"?
EVE PORCELLO: You're killing it. This is so good. All right. "Woman" is next. I was trying to think about what data to use for this oh, let me say one more thing. We used MongoDB not to be repeating myself over and over again, but it's worth repeating you can use whatever database you like, you can use Postgres, Fauna, you can definitely use that. Email leaked, that's funny. This is just dummy data, I don't think Dustin 37 exists, but I should email him later.
JASON LENGSTORF: Dustin, I'm so sorry.
EVE PORCELLO: I'm sorry, my mic wasn't plugged in. I'm messing up on many different levels.
We want to look at our "woman." It's funny to see people I know names'. They don't know. They're just going about their day. Basically what we want to do is use this JSON file to create a new type.
JASON LENGSTORF: We've got ID, first let me pull this up, first. I'm going to hide the sidebar and let's add type "woman." And, we have an ID. And that is going to be an ID. Or, should we make it an "int"?
EVE PORCELLO: Yeah, let's just make it an ID. That sounds good.
JASON LENGSTORF: Okay. And then we've got "first string." "Last string." And, "location string." Okay. So, then up here...we probably want to do "woman" and "woman." How do you want to query?
EVE PORCELLO: Let's search for them by ID.
JASON LENGSTORF: Got it. And then we need to add one of these. Or, we don't even need an async on this one, do we?
EVE PORCELLO: This is, straight up, pulling from a file.
JASON LENGSTORF: This is parent, args. We'll put in the context, I assume?
EVE PORCELLO: We can, sure.
JASON LENGSTORF: You tell me, you're the boss.
EVE PORCELLO: Definitely not the boss. We can put it in context, sure.
JASON LENGSTORF: Ice Cream Tango says: When should we use string type? Probably outside of the context of what we're talking about today. (Laughter).
EVE PORCELLO: That's like the answer I get to every question, "some other time."
JASON LENGSTORF: Do you have anything, specific, on extend type? Anything on Egghead?
EVE PORCELLO: Definitely. "Extend type "will help us put things in different files. I don't know that I have anything on Egghead about that, but I do have a blog about that or a project about that, not a blog.
JASON LENGSTORF: I'm going to drop this one, anyways, because if you haven't watched the Egghead stuff, it is all fantastic. Was it on Moon Highway, you think?
EVE PORCELLO: Hold on. I'm just sitting here. I can look it up, too. I don't know what I'm doing. So, it's schema...
JASON LENGSTORF: There's an "interfaces" one.
EVE PORCELLO: So, I have this project about it. I don't know if I have have an example. Let me share this repo that'll show you how to do it.
JASON LENGSTORF: While you're looking for that, what's a content hub? A content hub, in this context, we're pulling in from MongoDB or JSON. Should it be "underscore ID?" Lauren, that doesn't matter. The reason we used it for "person" is we wanted to match the Mongo DB. We are not doing that. We are just using our JSON data, which means that we can match this format. So, we kind of get whatever choice we want. It's up to us. That's kind of the beauty and also a potential foot gun of GraphQL, there are no rules. You can do whatever you want, which means you can be really, really flexible. It also means if you don't have you know. If you don't have some conventions in your team, you can get really out there and things can start to look really strange.
But, that being said, um, did you which
EVE PORCELLO: No, I didn't find that thing. So, I will look for it and I will share it somewhere. Why don't you drop me an email. It's just firstname.lastname@example.org. I'll find it when I have fewer windows open.
JASON LENGSTORF: There are a billion different great repositories to dig through for information.
So, let's get back to getting finding a woman. We have so, we have our data here. What we haven't done yet is pull that data in. How do you want to actually grab that? Where should we pull it from?
EVE PORCELLO: Let's just um why don't we just require it and we'll we'll just use it without context, so that we can I want to make sure we show the fetch thing, so, this will speed us up a little. If you add it to Line 3, you can just say, "cons women equals require the file."
JASON LENGSTORF: And then what we can do in here, then, is we'll skip the context and instead, we'll get the "ID=args ID." And return find that should work. And, then we can go up here, make sure that's right. Make sure that's right. Let's go do a query and we will get ID 2 and we'll get first and last name. Oh, no! Cannot return null for nonnullable field query woman. What did I do wrong? I probably just screwed something up. Let's see.
EVE PORCELLO: Let's see...
JASON LENGSTORF: Args ID. Passed in the ID.
EVE PORCELLO: Oh, we're looking for the username, not the ID. Oh, no! Scratch that. We are looking for the ID.
JASON LENGSTORF: Woman, find. And then we would get that's right. ID. Oh, you know what? I bet it's doing it as a string.
EVE PORCELLO: Oh, yeah. Get rid of that extra one. Our data could be better. (Laughter). I think, probably. But that'll work.
JASON LENGSTORF: I bet that solves it. There it is. We can solve that a couple ways. We could go up here and say that this needs to be an "int."
EVE PORCELLO: Yeah.
JASON LENGSTORF: Right? And then if I come back here, and we'll do triple equals again. And when we pass in 2, it works. This is where typing is both very cool and also can be a little bit of a stumbling block if you're not familiar with it. Person, woman. What's next? Person, woman, camera no.
EVE PORCELLO: I'm leaving it up to you. This is why we have to build this. So, the next one is "man."
JASON LENGSTORF: Oh, oh, oh. Okay.
EVE PORCELLO: So ridiculous.
So, the man, if you take a look at there's a file called "data details."
JASON LENGSTORF: Data details.
EVE PORCELLO: This is literally a cheat sheet of nonsense. (Laughter). So I was trying to think of a man that I could fetch some data about and I found this Ron Swanson quotes app. An amazing thing about GraphQL that I still get very excited about is you can fetch from REST APIs using resolvers. Instead of ripping all your REST APIs, instead you can just fetch and that's a pretty cool thing.
JASON LENGSTORF: Okay. So, I pulled in these. Do I need to install, like, Node Fetch or anything?
EVE PORCELLO: In the dependencies, we should have had Node Fetch, so let's install that.
JASON LENGSTORF: Okay.
EVE PORCELLO: I'm just going to act like that was intentional. Definitely want to install Node Fetch.
JASON LENGSTORF: Okay. So, then I'm going to go up here and I'm going to grab it. So, we've got fetch. We've got the stub of a man type and the stub of a man resolver. So, this is our setup here, so I can, what? Do "cons result equals await fetch."
EVE PORCELLO: Sure, yeah.
JASON LENGSTORF: Like that?
EVE PORCELLO: Yep.
JASON LENGSTORF: Okay. Then what?
EVE PORCELLO: And then we also need to make that function async, the man function.
JASON LENGSTORF: Yes, we do.
EVE PORCELLO: So, at this point, you can say ".then." So there's where that ".then" happens.
JASON LENGSTORF: Okay. JSON?
EVE PORCELLO: Yeah, it is.
JASON LENGSTORF: And this is just the regularold fetch API to Node. This is an amazing API. If you're not APIs and you're using something like [Indiscernible], I recommend using this. On Node, it's teenytiny and it gives you all the same features and benefits as something like [Indiscernible].
EVE PORCELLO: Totally. So, at this point actually, you want to pull up that URL, in your browser, just so we can take a look at it? The Ron Swanson thing?
JASON LENGSTORF: Okay.
EVE PORCELLO: So, what this returns is an array with a random quote in it. It's pretty simple. Every time we hit that route, we're going to return a random quote. So that's really what we're going to try to model with our data. We're just trying to return that quote.
JASON LENGSTORF: Okay. So, then here whoops we can do something like this.
EVE PORCELLO: Yeah.
JASON LENGSTORF: And then return that quote. All I'm doing here is restructuring. This is going to be "quote" and that'll be a string and that should be that. Let's try it.
EVE PORCELLO: Nice.
JASON LENGSTORF: Oh, no. I got to start the server.
EVE PORCELLO: That's what I get for not installing Node Fetch.
JASON LENGSTORF: Oh, I forgot to define the query.
EVE PORCELLO: The query. Yeah.
JASON LENGSTORF: Then we have "man" and that returns a string. Try again. There we go. And do "man." There we go.
EVE PORCELLO: That's a particularly funny one.
JASON LENGSTORF: That's good.
EVE PORCELLO: Nice. That's our quote. I wanted to keep it simple with our fetch request, but just know that if there's any data you need to get from anywhere, this is really good for, like, content APIs. So, if you're pulling if your content API has some sort of REST endpoint you need to pull data from, you can bake that into your schema and use your fetch as a simple way of getting that data.
JASON LENGSTORF: This is super cool. This is super powerful. You need to use the REST API, great.
EVE PORCELLO: Is there a performance penalty? If you are wrapping REST APIs with GraphQL, you do still you still are making that HTTP request to another API so that's a nice transition. Taylor, it's like I planted you there to ask that question, because there's a pretty cool tool, from Apollo, we can use to make performance even better with these fetch calls. So, that's next.
JASON LENGSTORF: What up? What are we doing?
EVE PORCELLO: So, next, we are going to everyone has been waiting for this. We are going to use the Best Buy API and just like we all thought we were going to today.
And we're going to we're going to create a wrapper around this REST API. So, yeah, let me slow down and let's go take a look at the docs. Very, very normal app that we're building. So, I was trying to figure out where to pull data from, about cameras and TVs and Best Buy has some really nice documentation. There's a product API and a category API and all these stores. So, I don't know. You could build something cool with this. I did have to request a token for Best Buy and it ratelimits me a lot, so it'll probably do the same for us. It'll work. We just have to hit it a few times and it'll start working. So, kind of fun.
So, yeah, we're basically just going to try to find a camera and then a TV and then we're going to pull it from this REST API. I have dug through this, and put together those links, in that file called "data details" that I made.
JASON LENGSTORF: Ah. Here's our whole thing. Which means we're going to need one of these. So, I'll go here and then we'll have to drop that in.
EVE PORCELLO: Yeah.
JASON LENGSTORF: Do you want let me pull offscreen and you can drop in that.env.md and then I'll pull it out.
EVE PORCELLO: Okay. Cool. And of course, I totally lost my API key. Let me find it. So...there we go.
JASON LENGSTORF: Where are you? I just lost you.
EVE PORCELLO: I'm back. I'm back, I think. Right?
JASON LENGSTORF: I don't know. I don't see you. Did the liveshare break? I see you in the index.js.
EVE PORCELLO: Okay. I'm coming back.
I guess I'll have to look at the link again. In the meantime, while I'm struggling with that, basically what we'll have you do is have you looked at Apollo REST Data Sources before?
JASON LENGSTORF: I have not.
EVE PORCELLO: This is me stalling, but for a purpose. I'm just going to drop that in an index or, wait. So, Apollo REST Data Sources are what we're going to use to wrap two different REST APIs. We're going to create a wrapper around the Best Buy API. Why would you use this, versus using fetch? We want to use Apollo REST Data Sources because they're going to help us handle caching and deduplication. It gives us a cache to work with. If I request the data and then I request it again, I can look at the cache versus going directly to make that HTTP request. So, that's what that's all about.
I'm going to drop in the key in the index.js file because I don't care about security.
JASON LENGSTORF: And I'm dropping it into the.env file and we're back.
EVE PORCELLO: We're back.
JASON LENGSTORF: I have the Best Buy API key stored here, which means that we need to let's see, I need to install that Apollo Data Source REST. That's already installed. We can grab that from the docs. It says REST Data Source from the require okay. REST Data Source equals Apollo Data Source REST. What do I do from here?
EVE PORCELLO: From here, we're going to create it. So if we scroll done above that start function.
JASON LENGSTORF: Above? Like, here?
EVE PORCELLO: Yeah. 68, around there. We're going to create a class that wraps the REST API. It'll be called "Class Best Buy API" or whatever we want to call it. Extends REST Data Source.
JASON LENGSTORF: Okay.
EVE PORCELLO: And then in the constructor, we will call [Indiscernible] and then I'm going to paste in this link for us because making you do so is too rude. And, this is kind of the base URL. It's like everything after that will specify which products we want. But for any request, it'll always have that main beginning of the URL, basically.
Now, once we've created this, we're going to create a function. And, it'll be an async function that's just going to be in charge of getting the data. So, we'll call it "async get written data." So, we're going to actually, let's call it "get camera." I shouldn't have called it "get data."
JASON LENGSTORF: Okay.
EVE PORCELLO: So now inside of this, we're going to use those long URLs that I pasted let me just help us with this. We're going to "cons camera."
JASON LENGSTORF: Okay.
EVE PORCELLO: And what this will do is we're going to call this "get" function and we're going to get a specific route. I'll type this out so it doesn't look like I'm copying and pasting too much, but I don't want to make you do it because I am a guest here and I should be polite.
So what we're going to do is I can't I made it, like, four characters. I'm pasting this baby in. There we go. So, this is a very long URL that is upsetting, but ultimately, it will get us the data that we want, which is cool. Yeah.
So, if we hit "save "on that, that will be too long of a URL for anyone to actually read. So, we have the category path name, so that's going to give us digital cameras.
JASON LENGSTORF: Okay.
EVE PORCELLO: We're learning a lot about the Best Buy REST API today.
JASON LENGSTORF: We're picking our fields here?
EVE PORCELLO: There's more than that, but that'll kind of give us what we need. We actually don't even need that description because I think that's been deprecated. I've also added this "sort by." That's going to come from our arguments. So, the arguments are going to be passed into that "get what is it called? "Get camera" function.
JASON LENGSTORF: So, as an object?
EVE PORCELLO: It's actually just as a value so we don't need the braces.
JASON LENGSTORF: Oh, okay. All right. And then we've got our Best Buy API key.
EVE PORCELLO: Right. So, then we're going to say, then we'll take in the data and return data.productszero. So, we only one product. One camera, right? Because that's what we're doing. And then we can return the camera.
JASON LENGSTORF: And so, the "get" is like a wrapper around the fetch API that does extra stuff for us? So, like, we don't have to run the response.JSON or any of those things? And if we run this more than once, it's going to cache that response?
EVE PORCELLO: Yeah.
JASON LENGSTORF: Cool. Cool. Do we want to add this to context?
JASON LENGSTORF: Hmmm.
EVE PORCELLO: There we go.
JASON LENGSTORF: Okay. And so now if I go up here to "types," I'm going to add "type camera." And I believe our fields were skew name, price, description, so I'm going to grab those.
EVE PORCELLO: Cool.
JASON LENGSTORF: And then, are they all strings?
EVE PORCELLO: Everything, except the sale price, which is a float.
JASON LENGSTORF: Okay. Float and string. Okay. And then, in the query, we want to add "camera." And this is just going to be, like, no arguments, right? No, we wanted the "sort by."
EVE PORCELLO: Yeah, so what we could do is we could say "sort by" and then we could use something like a sort category and that's a good place for GraphQL enumeration type so we'll it's kind of like a restricted list of options for a field.
JASON LENGSTORF: Like "sort category" like this?
EVE PORCELLO: Yeah. Exactly.
JASON LENGSTORF: Here we go. Here we go.
EVE PORCELLO: Nice. Typically, we'll say let's do two of them. "SKEW," all caps. And the next one is "NAME."
JASON LENGSTORF: This would be sort category and we're going to get back a camera.
EVE PORCELLO: Yeah.
JASON LENGSTORF: Okay. Cool. Cool. Cool. That means in our resolver, we need a camera resolver. We're going to get our parent and then we'll get our args and then where does the data source come in? Is it in context now?
EVE PORCELLO: It is in context now.
JASON LENGSTORF: Oohhh, magic.
EVE PORCELLO: Adding it to the server constructor will put it there. Wild.
JASON LENGSTORF: I'm making the assumption this needs to be async, is that correct?
EVE PORCELLO: That is a correct assumption.
JASON LENGSTORF: I'm going to do the "sort by" and we want to return context Best Buy API. Right?
EVE PORCELLO: Um, yeah. So, it's context.datasources.bestbuyAPI. We'll sneak data sources in there.
JASON LENGSTORF: "Get camera data" and we'll sort by.
EVE PORCELLO: Yeah. Nice, killing it.
JASON LENGSTORF: Do we need to map the enum to anything?
EVE PORCELLO: We don't. That's just going to work for us because it's a list of those strings. So no mapping required.
JASON LENGSTORF: Okay. So, are we done? Is it ready?
EVE PORCELLO: I feel like we're done. Yeah. Let's check it out.
JASON LENGSTORF: Here we go. Okay. Man. Camera. And then the sort by is skew or name and because it's an enum, it shows us what's available. So, let's go by name. Cool. And oh, I have to set fields. So, then we're going to get back skew, name, sale price and description. Oh, no! Cannot return null for camera description. Okay, let's just get rid of that.
EVE PORCELLO: Yeah. I feel like that was a recentlydeprecated thing that's messing me up.
JASON LENGSTORF: Look at that. Now, we have a $2,500 camera. (Laughter). GraphQL's got expensive taste. (Laughter). For the TV, we can reuse this data source, right.
EVE PORCELLO: Exactly. You know how long that link is. That's too long. So if we build that one time, then we know exactly where to where to go to start making some changes. So what we'll do, here, is you'll just create another one of those for getting a TV. Yeah. Love it.
JASON LENGSTORF: And then this needs to change, I assume?
EVE PORCELLO: Yeah, this one will be it'll just say "TVs."
JASON LENGSTORF: Perfect.
EVE PORCELLO: There we go.
JASON LENGSTORF: Okay.
EVE PORCELLO: I don't think anything else changes. Because that URL is so similar, we could probably do something more reusable with that, but that's okay for now.
JASON LENGSTORF: Definitely could, but we won't. (Laughter). And so then here, I'm going to do the same thing. TV. We can keep the sort category and we'll return a TV. Okay. Then, assuming all has gone well, I'm going to swap this out for a TV. And, I broke something. Nonnullable query TV. Did I can forget to add it to the query?
EVE PORCELLO: Ummmm...let's take a look.
JASON LENGSTORF: TV, sort by, sort category.
EVE PORCELLO: Did we do the TV type?
Sale name, price, description.
EVE PORCELLO: Oh, yeah. That pesky resolver. That's nice about GraphQL, it's always going to let us know. It's that type system. It doesn't have data because we didn't ask for it.
JASON LENGSTORF: That look at it go! Okay. There we go.
EVE PORCELLO: Yeehaw.
JASON LENGSTORF: That TV is very affordable.
EVE PORCELLO: Wildlydifferent taste in TVs and cameras.
JASON LENGSTORF: This is a hipster living in Portland that's like, I'm a photographer, but I don't watch TV.
EVE PORCELLO: I mostly read.
That's awesome. That's working. The data source gives us this nice, little reusable wrapper thing, which is sweet. As I said, handles things like caching and things that we would have to build in order to use Fetch, which Fetch is awesome for many things but if you are noticing performance hits, you can use something like this.
JASON LENGSTORF: And a thing to point out, the first time we ran this, it probably took a second to finish and subsequent hits are 200 milliseconds. You can feel the impact of that. And I actually don't know if the slowdown is coming from this Mongo hit or not. There are tools this is way beyond the scope of what we're doing today, you can do tracing, in GraphQL, to the field level of how long it took to resolve each field, which is really, really cool and gives you really good visibility into, like, if you do have a slow request, you can figure out, oh, yeah, it's the person the person is slow so we should, you know, we should look into why that connection is slow or something like that.
Which is very hard to do with REST. So, uh, yeah.
EVE PORCELLO: There's also a tool called Apollo Studio, which used to be called Graph Manager and Apollo Engine before that. You add it as a key to your Apollo server and this will give you metrics for that sort of thing, so that's really cool. Because then I can see, okay, we got good performance on camera and TV, but "person's" slows us down. All of that can help out a lot.
JASON LENGSTORF: So okay. I think we're done, right? We've got it. Person, woman, man, camera, TV.
EVE PORCELLO: Person, woman, man, camera, TV. We've done it.
JASON LENGSTORF: What is "parent"?
EVE PORCELLO: That parent thing hello, parent! It's going to allow us to basically look at the parent object. So, if that makes no sense, that's totally fine. Let me show you and then it hopefully will. So, the best place to probably demonstrate this is with our women. So, we only have an ID, a first name, a last name and a location here. But what we can do with that URL field sorry, what we can do with that parent is we can add another field. So, let me show you what this looks like.
Um, so, for example, the woman, right. If you wanted to add a URL for a string that returns a string to the woman
JASON LENGSTORF: Uhhuh. Sorry, you wanted me to do that. I was just watching. (Laughter).
EVE PORCELLO: I was like, I'm supposed to let you type and then I just take over I do that at class, too, when I'm allowed to be at class with people. I use their computer and then apologize to them. Yeah, I can type the rest.
The URL, here, we've added a field for "string." We don't have that anywhere in our database at all, but that's okay because we can use that parent to help us out with that. So, let me show you what I mean. Down here, in our resolver map, we have our woman, so that's related to the query. But let's add a resolver for "woman." I want to make sure that I'm in the right spot. So we're outside of the query resolver's object
JASON LENGSTORF: We can collapse this and so it sidebyside.
EVE PORCELLO: We can collapse it. That's not the right button to click. Yikes. Cool. So, I'm just going to scroll down. This time for "woman" we're going to add a field called "URL." URL returns a string, so it could just return a regular, old string. So, hey, I'm a URL.
JASON LENGSTORF: And this is really we're about to touch on something that I think is one of the cool features of GraphQL. I'm going to refresh the page, here, and I'm going to grab this. Here we are, we have now we're getting a URL and so that may feel like magic, what just happened. But check this out. Here's what's happening when we're making these queries. When we return this person, what it's doing is it's taking the object of the person and it's saying "in the person object, look for fields with these properties." And, if you find them, return them. Right? So, effectively, all that's doing is if we were to look at whatever resolver for "woman" looks like, we've got the first. It would be just be "first" and then we get that parent. Right? And then it would just be parent.first. Am I correct in this?
EVE PORCELLO: Yeah. Yeah. Absolutely.
JASON LENGSTORF: Ah, it's beautiful. It's beautiful. But, GraphQL does that for us. So we don't have to do that work. I got carried away. I got so excited.
EVE PORCELLO: It's exciting. This can return any string. I can say "/name.html." That's great and all. Would you mind running that query again for me?
JASON LENGSTORF: Yep. There.
EVE PORCELLO: Cool. So now every time we have that, it's going to add it. If we change the ID for the woman maybe to 3 or 4 or something it will add that for the URL for that person, as well.
JASON LENGSTORF: Uhhuh.
EVE PORCELLO: Here's where the parent thing gets fun. So, I'm going to pass in "parent." Then I'm going to say "parent.first. HTML." So we're creating a custom URL based on data from the parent object. So, every time I ask for that field, every time I query URL, I'm really asking for I'm asking for this function to be called. So, the parent allows us to tab into that object. This is a simple example because we're using the the JSON file for this, but we should know that every time we want to connect data, anytime we want to set up weird data relationships between different types, we can use this parent object to kind of act as a lookup between the different data types. So, a lot of power in this parent object. Sometimes people call it "underscore." Sometimes people call it "object." I'm in the habit of calling it "parent." Some call it "root."
JASON LENGSTORF: So people are asking, in the chat, is it like this or by default? The way that I always think of resolvers is that when you define a GraphQL schema, you are created a nested column of, like, functions, basically. And so, the when you call the query, the query is a function and then it looks for this and then it looks for a function called "person "in the resolvers and for each field, it runs a function that runs this field name and passes down whatever the thing is. So, whatever the result of "person" is, it'll pass down to name. So, it just kind of, like, progressively passes down data so that you can do things, like Eve was describing here.
EVE PORCELLO: Yeah. That's the graph part, right? The graph part of GraphQL, where we have different data types we can connect. I love that. That's great.
JASON LENGSTORF: I think this is our stopping point. We're just about out of time. So, if someone wants to go and learn more, where should they go next? Like, what would be a good next step from here?
EVE PORCELLO: Um, a good next step would be just to kind of try this out with your own data sources. I think getting handson experience with an Apollo server this kind of represents a few different types of data, but you can use your favorite database and connect up all sorts of different sources, so that's a good next step.
Um, yeah. I think
JASON LENGSTORF: I'm also going to recommend, go follow Eve on Twitter and make sure you go check out all of the places where she teaches because this is just the tip of the iceberg. If GraphQL is something you want to learn, React, go learn. The doors this is going to unlock for you are unbelievable.
Anything else you want to link to? Shoutout?
EVE PORCELLO: We do send out a newsletter. We post articles on our website, as well. You can find it on any one of the articles. We have an exciting, new GraphQL course coming out. I can't say too much about it right now. But I will I will just say that it is very fun, very indepth and I can't wait to get it out to you all. So, yeah, just keep an eye out for those types of announcements.
JASON LENGSTORF: Super, super exciting. One more shoutout to White Coat Captioning, which is made possible by Sanity, Fauna, Auth0. Make sure you go and check our schedule. We've got so much exciting stuff coming up.
Next week, we're going to do something that's going to melt my brain. We're going to take about observability. This is lowlevel server, how to make sure things work. This goes beyond logging, beyond what you've dealt with before. I'm so excited about this because it's an area I've always wanted to know more about. Shelby is going to give us a great introduction to this, show us how to wire it up. We've got Monica and she's going to talk to us about Web Mention. It's a way to turn the whole internet into your comment section, which is really, really interesting. So, make sure you tune in for that one.
Browse through the rest of the schedule. The episodes that we had to reschedule, last week while I was out of my house, has been rescheduled. Ben is and Emma coming back.
With that, I think we're going to call it a wrap.
Eve, thank you so, so much for hanging out with us today.
EVE PORCELLO: Thank you so much, Jason. This was awesome.
JASON LENGSTORF: Chat, stay tuned. We are going to raid and we'll see you next time.
Closed captioning and more are made possible by our sponsors: