Check out a free preview of the full Headless CMSs with Next.js course
The "Pulling Content into Next.js" Lesson is part of the full, Headless CMSs with Next.js course featured in this preview video. Here's what you'd learn in this lesson:
Scott demonstrates how to fetch data from a CMS using GraphQL queries. He explains how to type check the returned data and dynamically display the fetched content on a Next.js page. Scott also briefly discusses caching with Next.js and how to break the cache to ensure that updated content is displayed.
Transcript from the "Pulling Content into Next.js" Lesson
[00:00:00]
>> Scott Moss: Once that query is copied, I am then going to go back to our repo. Inside of our content folder, I'm gonna make a new file, I'm gonna call it queries.
>> Scott Moss: So in here, we're gonna import our fetcher, so, content, GraphQL fetcher, everywhere, bring that in. And then we're just gonna export queries for our components, our pages in here.
[00:00:26]
So I'm gonna make one for the hero, so I'll say getContentForHero, like this. It's gonna be async.
>> Scott Moss: And I'm gonna make a query, so what is a query? Again, you don't really need to know GraphQL for this, it's literally just a string, I'm gonna do backticks, I'm gonna add this comment here with a hashtag.
[00:00:56]
And the only reason I'm adding this is so I get syntax highlighting. So if you do the #graphql at the beginning of the string, your editor will start highlighting the string like a GraphQL query. It won't change the behavior, it'll just make it highlight. Then I go to the next line and I'm just gonna paste in that query.
[00:01:23]
>> Scott Moss: And that's it, now we have the query that we want. Then I need to pass this query to our content GraphQL fetcher. So I'll say data = await contentGqlFetcher. So, I just broke one of my rules, which is if a function has more than three arguments, it should take an object.
[00:01:46]
This has three arguments, it does bother me, so we're gonna, I'm gonna change that. I don't like that, so I'm gonna go here, I'm gonna make this taken an object with all these properties instead cuz that's gross. So it's gonna take in query, variables, preview like that. I'm just gonna go ahead and add the types for it as well, you don't need the types if you don't wanna do all that work, I get it.
[00:02:09]
It's exhausting for sure.
>> Scott Moss: And then preview, come here. Variables, any and then preview fully, okay, so we got that. Need a space there, there we go, okay, so now I made that an object. The other thing I wanna do is, I wanna type check our content that comes back, I mean, otherwise, like you're just gonna be looking at console logs all day like, what's that?
[00:02:50]
That's the field on that thing again. So, what we can do is we can add a generic to this contentGraphQlFetcher to tell it what type of data is gonna come back. Given the query that we passed in and then we can pass that, we can make that a return value here.
[00:03:10]
So, if you don't know what a generic is in typescript, it's like an argument for a type. It's like, I need this part of this type is gonna be dynamic, so I wanna make an argument for it and the way that that looks is, I actually have it right here, not right here, but,
>> Scott Moss: Right here, we know that this function is gonna return to promise because that's what JSON does or fetch does.
[00:03:35]
And all we can just pass in a generic like this, so that's basically what we're gonna do. So I can go here, I can say, it's gonna return a promise. This promise itself takes a generic, so it's gonna be of type T, which is like a placeholder. So, let's say type T or it'll be undefined like that.
[00:04:02]
And then all you have to do is just put the generic up here at the beginning of the function with the same name, so like that, we have a generic. So, now this is saying, this function returns a promise that when resolve will be type T, and type T is whatever you pass in when you call the function.
[00:04:27]
Most generics, they use T as a placeholder, and then down here at the bottom, I can just say return data as T.
>> Scott Moss: Okay, so now I have my generic, ready for some types here in my GraphQL fetcher function called pass on my object pass on my query.
[00:04:52]
There we go, so we got that, and I probably just wants to be to like pass an actual generic here. So we can pass in a generic here, we don't really have a type right now. So, let's go make one and this is where it gets really easy.
[00:05:03]
So all we have to do really, if you just copy everything in here underneath the query.
>> Scott Moss: And what I like to do is on the root, I like to make a file called types. And I'll export a type, so I'll say type HeroQuery, and I'll set equal to an object like this.
[00:05:28]
I'll just paste that query in there. Then I'll just fix it, so that's an object type, this is an array of objects, so I'll go out here and put an array here. This is a string, this is a string, this is a string. This is an object type, this is an array of objects.
[00:05:59]
And this is a string, and this is a string, there we go, that's my type. All right, so now we have our types, I can go back to our queries. Instead of passing any, I can do HeroQuery like that. And now, this data is type checked, all right?
[00:06:15]
So if I were to say data.HeroCollection.items, which is an array, so I can say give me Thing in there, .preTitle. It's type checked, it's great. It's beautiful, okay? So now we'll just say if not data, we wanna throw some issue, throw some error here. We're not handling errors right now, but oopes, and then I'll just return.
[00:06:45]
>> Scott Moss: The data. No, all right, well let's add this to our hero then. So let's go to our hero and we might run into some Next,js problems. I hope we do so we can solve them, cuz I think there's one issue that we're run into that's quite interesting.
[00:06:58]
So we are in a server component because there is no use client at the top. So that means, we can make this async like this. And we can just call our function our query in here. So I can say data = await getHero, or whatever I call it?
[00:07:19]
GetContentForHero. getContentForHero() like this. Now we have our data, but because that's gonna return a list of heroes, even though we have one hero, I'm just gonna kind of scope it. So I'm gonna say content = data.heroCollection.items 0, so that'll actually be the hero right there, so now we have that.
[00:07:47]
>> Scott Moss: Yes, you probably don't wanna go around assuming you're always gonna have stuff in here. So you wanna make sure that there's checks and things involved like that, especially when there's other people modifying data on a system not connected to this. But for now, we're just gonna assume that it works.
[00:08:02]
And then as you can see, we still got the type check. So got a call to actions, we got our pretitle, subtitle, title.
>> Scott Moss: So now we're gonna go ahead and just replace these strings. So for this one, this is our pretitle, API Studios Now and Beta. Nah, this is now content.preTitle and API security framework, nope.
[00:08:28]
This is content.Title.
>> Scott Moss: All this stuff right here, this is now content.subtitle.
>> Scott Moss: Let's start with that. We'll come back to the buttons in a second, cuz I'm pretty sure there's going to be some broken stuff, and I wanna talk through that cuz it's unique to Next.js and you're gonna run into it if you're doing CMS stuff.
[00:08:54]
So let's go see what what is broken. I guess I need to start the site first, so we'll do that. There we go.
>> Scott Moss: Try this again, so we'll start this. Go to our page. I guess it just works, nothing broke. I thought we were gonna run into an issue with something, but as you can see, now my content is here dynamically from the page, it's magic, right?
[00:09:27]
So let's go through whole flow. Let's go back to our CMS, okay? I'm going to,
>> Scott Moss: Which one is it? I have so many contentful things open. Okay, I'm just gonna go here. So I'm gonna go ahead, I'm gonna edit it again. I'm gonna say, okay, we're the best, really.
[00:09:56]
All right, I'm gonna hit publish. We have to keep hitting publish for now. We'll fix that in the data later with previews, so I'm gonna hit publish. And then I'm gonna go back to our site.
>> Scott Moss: And what do you expect to happen when I hit refresh?
>> Student 1: Give you content.
[00:10:18]
>> Scott Moss: The new content that I just published not trick question. [LAUGH] That's what I would. Yeah, that's what I would expect. I didn't, why? Why did I not get it? Is there something wrong with contentful.
>> Student 1: Cached.
>> Scott Moss: Yes, it's cached. Next.js is caching this. Don't worry, I can do a hard refresh and we'll get it now.
[00:10:39]
So that's something to keep in mind of and that's important because, in production, you want your content to change when you publish it without having to rebuild, but if Next.js is aggressively caching things, how you break that. So we'll talk about how you can do that with some of the webhooks and revalidation strategies in Next.js.
[00:10:59]
Exactly something I covered in immediate Next.js course, where we have to like revalidate all the caching things. We won't have to talk about how to implement it, but we will talk about how to, how to break it basically. So yeah, that's a cache. Okay, let's add the buttons.
[00:11:14]
So for the buttons, we can go down here. And where are they? They are, okay, so here's one, right here. It's all scoped into the div, and then we have another one that's scoped. So here's the thing. We have two buttons. So there's a couple ways we can do this.
[00:11:39]
And they're different, like they're also they're styled very different as well. So do we want to say that the second button is always gonna be styled this way and you can change out the, the actual content in the URL. Or do we wanna say that it doesn't matter if this, the second button is styled that way.
[00:11:56]
So that's like some of the stuff you have to think about because those are fields you'll have to add in your content model. If you want the non technical person to be able to have the responsibility to make that change, if you want them to be able to change the order, basically, are we gonna ignore order or not?
[00:12:12]
That's what it comes down to. So I think what we're gonna do here is we're just going to- Button type. Yeah, you can have a button type, yeah, but you see, every field that you add it increases the complexity. So it's like in our head, that's the thing.
[00:12:26]
It's like a database schema was not meant for a human to consume. It's meant for an API, it's meant for code, it's meant for an ORM. It's abstracted in a way, it's named in such a way. So when you're like designing a content schema, it becomes very database like, but now a human has to directly interface with that.
[00:12:49]
And that's where it can get tricky. I've seen so many times where developers making stuff for their constant team, and they'll do it away and then it cost us like struggling and developers like, well, you mean it's like this. How do you not understand that? It's an icon logo type.
[00:13:03]
[LAUGH] They're like, what are you talking about? I don't even know what that means. So it can get crazy. Let's just assume we will keep both of these here, we won't iterate over them. We won't loop over them. And we'll just say, whatever the first button is in the collection goes here.
[00:13:19]
And whatever the second button in the collection goes here. We'll just keep it that way. So what does that look like? So let's declare them up top. Let's just say button one or a cta1, that's gonna be content.callToActions.items[0] and then cta2. That's gonna be that one. There we go.
[00:13:46]
So now I can go down here. I can say cta1.label, and then cta1.link
>> Scott Moss: Same thing down here.
>> Scott Moss: cta2.label.
>> Scott Moss: cta2.link. Cool, I got that callback here. I think mine actually say says getting started and read the doc. So let me go change it so we can see a difference.
[00:14:31]
It literally does say that. Hi.
>> Scott Moss: There we go. So now I have to publish both of these. So I have to click Publish. I have to click Publish.
>> Scott Moss: I don't have to publish the main one, just the relations. I go back to the site, do a hard refresh, which for me on arc is command SHIFT + R on a Mac.
[00:14:56]
Ooh, there we go.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops