Next.js Dynamic Routes With Contentful's GraphQL API
Next.js has a popular approach for generating dynamic routes. In this episode, Salma Alam-Naylor (a.k.a. whitep4nth3r) will teach us how to use Contentful’s GraphQL API to power them.
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: Hello, everyone, and welcome to another episode of Learn With Jason. Today on the show, we're bringing in the one and only whitep4nth3r. Thank you so much for being here.
WHITEP4NTH3R: Hello. And thank you for having me. Welcome, everybody. It's great to be here finally. I'm feeling good. It's Friday night. The work week is done. I'm ready to go.
JASON: I'm very excited to have you on the show because you are also a streamer and you are a very established streamer. I would say that you are probably, like, this is an honor for me because you're coming from
WHITEP4NTH3R: No. (Laughter)
JASON: Oh, is that a Jammy award winner, Cassidy asks? Indeed, it is. Congratulations on that, by the way. That was that's a big win.
WHITEP4NTH3R: Thank you. I had I was up against some big names. I was totally convinced that it was not a chance that I would win, so I feel humbled, honored, and proud. 2021 is turning out to be an interesting and good year.
JASON: I'm happy to hear it. Yeah, I mean, and you're doing some very cool things. So, well, instead of me trying to, like, poorly explain everything, I should let you speak for yourself. So, for folx who aren't familiar with you, do you want to give us a little bit of a background?
WHITEP4NTH3R: Okay. Way back when, I was a musician and I used to teach music in secondary schools, that's high schools, and I quit that and I was a comedian. And then I got into tech. So, I was a front end developer for a couple of years. I moved quickly up to being a tech lead and lead engineer, and then I decided that the further you go up in tech, the less tech you do. I got into tech to do tech.
JASON: Oh, my God, you're hitting so many of my feelings right now.
WHITEP4NTH3R: So, at the beginning of the pandemic, I thought, how can I keep up with my tech skills? And I somehow discovered that people streamed tech on Twitch. And I noticed that there weren't many women doing it at the time, and there weren't many people doing front end development at the time. So, I thought, there's a gap in the market. Let's do that. And I did that. Here I am. Now I do dev rel for Contentful. I stream and I love it and it seems to be a fun thing and people like it. That's my history.
JASON: No, it's great. Yeah, so, you mentioned Contentful. So, today, we're going to play with a little bit of Contentful. We're also going to play with Next.js. So, this is kind of your, like, area of expertise, right? You kind of so, I guess what do you like about the platform? What gets you excited when you, you know, what makes you choose it?
WHITEP4NTH3R: So, Next.js, for me, has a ridiculously easy and relaxing and comforting developer experience compared to other frameworks that I've tried. And the intuitiveness of it and the ability to scale with Next.js, even though I generally build products that don't need to scale, is actually, like, pretty powerful. Coupled with something like Contentful, which is just a headless CMS, content management system, that gives you JSON. You could actually be really, really clever with how you architect your applications and really lean with a GraphQL API and how you fetch your data. So I think GraphQL coupled with Contentful coupled with Next.js has just been my goto. That's why I seem to be a lot more experienced in it over this year, because I've built a lot of projects that have benefitted from using this combination, and I'm learning new stuff about it all the time as well. I don't think I've ever been able to get this deep into anything in my tech career. And I'm really enjoying that as well, because I get to learn new stuff when I build new stuff. I'm trying to find, like, new, innovative things to build to try to find new ways to use the framework. I love that it's open source as well. I actually made my first contribution to Next.js this week.
JASON: Oh, congratulations!
WHITEP4NTH3R: So, that was cool. So, I'm looking forward toe seeing what happens, where it goes, and just making sure that everyone knows about how cool the developer experience when you're working with Next is.
JASON: Yeah. Very cool. Yeah. And so, today, we're specifically talking about their dynamic routes, which is I think a new it was a I think Next was the first one to bring out this feature, right?
WHITEP4NTH3R: Yeah, so, and I don't think it's that old either. I think it might be just over a year old, maybe.
JASON: It all happened so fast.
WHITEP4NTH3R: I know. And so I built React applications in the past. Actually, a lot of my Twitch overlays are built in React and I've got quite a lot of routing to a lot of different URLs. You don't know if it's going to work. How do you pass in query parameters? I still don't really know. And with Next, you just put a file in a folder. You just create a folder structure and the dynamic routes, though, that's where it really comes alive. Because you can create thousands and thousands and thousands of page with three lines of code. Fetch your URLs from your CMS or your database or whatever or hard code them in JSON, tell Next to cycle around those, and then at build time, Next creates them.
WHITEP4NTH3R: You can pass dynamic data based on that slug or that route name or whatever on each single page, statically generated. And also, what I do want to get into later is there is also ways, like, thinking about if you have thousands and thousands and thousands of pages that you want to render at build time dynamically, that's going to increase your build time considerably. If you've got lots of editors working on your application and publishing content, your application's gonna be in a constant state of build. But Next also provides you ways to get around that by, for example, you can set fall back pages where you can say just pregenerate my top ten pages, and then you can serverside render the Next the other pages so you're not so you can, like, reduce your build time and things like that. So, they've thought about this. Next.js I always say is static first. As static as possible. But it provides you so many other flexible ways to fetch data, to be really clever with how you serve the content to your users, depending on the type of data that it is.
JASON: Mmhmm. Yeah. I really like that a lot. And so the other thing that we're gonna use today, right, is some GraphQL. Which I think GraphQL is one of those things that it can occasionally be polarizing, but I think in general, the web development community tends to both love and fear GraphQL, right?
JASON: What's your what's your general sense of GraphQL as a I guess as a protocol? I don't even know what the right way to describe it is.
WHITEP4NTH3R: Language? So, a lot of people are very used to REST APIs, aren't they? You query a resourcebased end point and you get a bunch of stuff back that you might not even care about. And say if you are constructing a page made out of ten components that uses ten different data sources, you need to make ten API calls if you're using a REST API to all those different bits of data.
JASON: And that's assuming they don't have any subqueries, too, like posts that have list of comment IDs.
WHITEP4NTH3R: Exactly. Yes. With GraphQL, you can actually make that you can actually request that data, query limits not withstanding, you can actually request those ten pieces of data in one API call.
WHITEP4NTH3R: I feel like GraphQL gives you so much control. As a front end developer for in terms of what you get from the backend. I remember when I was a tech lead, there was a lot of talk between the backend and front end developers, like, okay, backend, you need to give me this in this call and you need to give me this in that call. And there was all these contracts, right? Whereas in GraphQL, front end developers don't really need to have that conversation as much. Yes, you need X, Y, Z available on the API, but you don't need to say, oh, can I have an extra API call for that page, please? Because I only need an ABC rather than the whole alphabet. And I think that is really putting the power in the front end developer's hand of their own architecture of their application.
WHITEP4NTH3R: I'm a big fan of obviously backend and front end working really closely together, but I think this empowers front end developers a lot more. I think it's really nice for beginners, because I think with REST APIs you can get a lot of data back that you have to traverse through on the front end that you actually don't need. You can actually do transformations with GraphQL on the API call after the fact. Therefore, it greatly speeds up your front end applications and reduces the need for so much logic in your front end application as well. So, like, fewer bugs, right?
JASON: Yeah, and it's one of those things, you know, just said in the chat, it can feel like overkill if you have, like, one call in your whole app to set up a full or a full GraphQL API. And that's true. Like a lot of times when I'm building something, I'll still use REST because I have, like, one thing I need to do. I'm not gonna set up a whole GraphQL instance for that. But as soon as you've got, you know, multiple queries and some of those queries share data and you have to get nested calls, like, the example that I always use is if you have, like, a blog and your blog has a list of posts, so, you get one post, that post comes back with an author ID reference because you normalize your data if you're a, like, dataconscious developer. So, now you've got an author ID. That's two requests you've got to make to get the author's name. Then you get the list of related posts. That's another list of IDs. Let's say you've got four. Now you're at six calls. Now you're gonna get the comments. There are 150 comments on this post. Now you're at 156 REST API requests, and that's assuming you don't need to make an additional one to get the comment author data. So, this becomes this exploding problem of complexity, and that's for one blog post, right? So, you can imagine as it gets into ecommerce and you've got products, reviews, ratings, all of these nested pieces, you're, like, making thousands of API requests for one product. Like one product page. And GraphQL just all that, it shrinks down, it's got builtin caching. It's got all of this deduplication. You can do all these amazing things that just make it, like, friendly to use. And what you mentioned about front end and backend. I remember when I was at IBM, we were looking at, like, how do we solve that problem? We had this issue where there are dozens of teams, and each one of them's maintaining an API and that API is getting all these bespoke REST end points because each UI needs a slightly different thing. The UIs evolve and forget to tell the backend. They don't know whether or not they need to update like do we have to maintain these 35 bespoke REST end points we created? When we introduced GraphQL, it all went away. Hey, make sure this schema is valid. We had tracing through where we could see exactly which fields were getting called. When they decided if they were going to remove a field, they could see 0 people have requested this field. Good, get rid of it. We don't need to support that data anymore. It's got a lot of power when you get into complex use cases.
WHITEP4NTH3R: You can even say that GraphQL is more environmentally friendly on resources and energy than REST. Because of the nature of what you were just describing.
WHITEP4NTH3R: Environmentally friendly in terms of developer hours. You don't need to maintain those 35 different REST end points and spend all that power doing all that work. Then with API calls and server resources, especially when you have a very largescale application. Similarly as well, if you are just starting out and you're just starting out on the Jamstack and taking advantage of all the free tiers that all these, like, services provide, like Netlify, Contentful, if it you're using GraphQL, you're gonna make fewer API calls. Therefore, using fewer of your limits, meaning you can do more and go faster without having to invest at that point.
WHITEP4NTH3R: Which is great if you're just a hobby developer, right?
WHITEP4NTH3R: This is the kind of thing I've been looking at a lot this year as well, trying to work on the Jamstack in that kind of hobbyist way to be able to share the innovative ways you can scale on the Jamstack for free as someone who is just starting out who can't afford it. You know, I'm trying to make tech accessible to all. I guess GraphQL makes that kind of tech more accessible because you don't have to, you know, run up all those resources and start paying.
JASON: You know, everything you said just resonates so much with me. And, like, being more environmentally friendly, being able to start for free, and kind of scale, like, that's why I am where I am. That's why I like working at Netlify. Is because I'm I just love the idea of, you know, it should you should pay for it when it makes you money. Like you shouldn't have to pay to learn. You shouldn't have to pay to experiment or validate an idea. You should be able to do all of the things that let you discover whether or not something is a good call for you on a free tier and then as you grow, that's when you should pay. Like, oh, you're making money now? Good. Pay the services that allowed you to make money. That's a good exchange of value. It should never be, like, oh, you want to learn something? Give me $10.
JASON: But, no, okay. So, let's talk a little bit more concretely about what we want to do today.
JASON: So, Jamstack Conf just happened. It was a ton of fun. I don't know hey, chat, who went to Jamstack Conf? Can I get a what's the right thing to put in the chat? Like an "F" if it goes bad.
WHITEP4NTH3R: Yeah, no, all that came to my mind was "F". What do we do? A "B". Let's do a "B" for a ballin time! (Laughter)
JASON: I love that.
WHITEP4NTH3R: I'm gonna put a "B" in chat for the good time that I had. At Jamstack Conf.
JASON: I will also put a "B" in the chat. (Laughter) Ballin. No, this is great. This is really, really great. Yeah, so, today, we wanted to play with Contentful and Next dynamic routes and GraphQL. You had a good idea, which was why don't we put together a list of, like, our favorite pages. Our favorite things from Jamstack Conf. So, we just published the video. So, anybody who didn't get a chance to join, you'll be able to go and watch those videos if you want. What is playing? Something just started playing on my computer. What is happening?
WHITEP4NTH3R: It's the "B". The "Bs" are too powerful.
JASON: Your stream just started playing in the background. (Laughter) I have I have your Twitch page open for when we switch over to desktop view, and for whatever reason, it's been open for this whole time, and it just started playing your intro video. So, that's a surprise to me, I guess. Okay. So, anyways actually, maybe this is as good a time as any to switch over to that view. We can actually start working on this thing, which I'm very excited about. So, let's do the thing. Here we are. Okay. There we are. Everything's loaded. Okay. So, before we do that, let's talk a little bit about our captioning today. We've got this episode, like every episode is being live captioned. We have Jordan here with us from White Coat Captioning. Which is always a pleasure. You can watch that right on the homepage of the site. Learnwithjason.dev. That is made possible by our sponsors. We've got Netlify, Fauna, Hasura and Auth0. Today is Hasura's last day as a sponsor. Wave goodbye. We're going to send them off with a smile. If you're looking to sponsor a stream, I've got an opening. Okay. So, anyways, also while you're clicking things on the internet, make sure you go and follow whitep4nth3r on the Twitter. And we're also on Twitch. So, let me do the this one. Did that work? Nope. I have one of these and it does work and I always get it wrong. There it is. Okay. Yeah. So, there we go. Supabase would be a cool sponsor. I would love to get Supabase as a sponsor.
WHITEP4NTH3R: That was one of my favorite talks at Jamstack Conf from John.
JASON: Yeah, okay, we'll make sure we add it to the list. Okay. So, speaking of the list, I believe I got an invite from you to a Contentful space that I definitely haven't joined yet.
JASON: So, let me go and do that. Where else should I so, I guess let's look at, like, Next.js.org. This is so, this is Next.js. We're going to be working with that. And then we're going to be working with Contentful. So, everybody go and look at that. Then we have where is the space?
WHITEP4NTH3R: While you do that, I'll just say hi to all of my regulars who are in your chat shouting at me and saying hi. How are you doing? Nice to see you. They've missed me because I haven't streamed all week, you see. So, they're catching their p4nth3r at a different time.
JASON: Okay. I've been invited to an organization. Let me see can I log in and make sure I get to this one? Copy link. Okay. Now I'm almost positive that I have an account already. Not under this email address. So, apparently I have to can I change the email?
WHITEP4NTH3R: I can invite you to a different email.
JASON: Yeah, it might be easier. Yeah, it's Jason@Lengstorf.com is the one I have an account for already.
JASON: Realizing now that I definitely should have done this before I went live.
WHITEP4NTH3R: .com. Thanks. Add you to this space. I called it Learn With Jason. It was a very boring space name. But there we go. Send invitation. One user is being invited. One user has been invited to your organization.
JASON: I'm so ready. I'm watching my inbox. Here it is. Okay. So, now I'm going to come back here. We're going to try this again, and I actually have an account for this one. Heyo. All right.
WHITEP4NTH3R: There we go.
JASON: So, I no longer know what to do.
WHITEP4NTH3R: Okay. So, here is Contentful. So, Contentful is a headless CMS that offers you a wonderful web UI to manage your content, to set up your content model, which is like your content structures and your types. So, whether it's like a blog post or an author or an event or something else. And then you can also what we're gonna do is install a Contentful app to explore our GraphQL schema as well. Because you can do that all inside Contentful.
WHITEP4NTH3R: So, let's go to the content model tab.
JASON: Content model.
WHITEP4NTH3R: And this is where we're gonna add our content model. So, this is where we're gonna add the shape of the data we're gonna store for whatever you wanted to put on the front end, which is like Jamstack Best Moments or whatever. So, let's call it let's call it we can call it anything. Like a post or a memory or a video or a
JASON: I like memory. I like that.
WHITEP4NTH3R: Memory. Let's call it a memory. There you go. Remember that the API identifier that has been auto generated we'll be getting later. With GraphQL, you don't have to remember it because GraphQL is so selfdocumenting and stuff. So, let's press "create." Now it's time to add some fields. So, we have to decide what we want to store on each memory. And if you click the "add field" button, there are a variety of different types of fields that you can choose depending on the data and the types of validations and the appearances that you want. So, let's start nice and easy with just text for the title. Let's give it a title.
JASON: Short text?
WHITEP4NTH3R: Short text is fine for that. You don't need a list. Then if you click the "create and configure" button, here is a nice little thingy. What I always like to check when I add a title is that check box there that says this field represents an entry title. The table will show up as the nice title in the UI. On "validation" tab, here's pretty cool. I like to pretty much set most of my fields to "required" because it makes front end coding a lot easier. I mean, obviously, some are going to be optional. When most of the base ones are required, you don't have to do "if exists." You can limit the character counts if you've got a particular type of UI where you only want, like, 20 characters or something like that. Which is really cool. A new thing that's come out with Contentful recently is the default value tab as well. So, if you've got editors who are doing the same things over and over again with lots of the same data, you can prepopulate your fields with a value, which is pretty nice, if they're the same a lot of the time. Then on the "appearance" tab, you can just choose how the different type there will be different options here for each field type. So, let's just keep single line for that. You can also add "help text" to your viewers editors, if you like, to tell them what kind of data they need to put in there. So, let's press "confirm."
WHITEP4NTH3R: So, I know you wanted to add a YouTube video. So, we could add the YouTube ID on to the video that we want. I think this one is just gonna be a text. Do you know what I want to show you? I think it would be better to add the full YouTube embed URL. Because then we can use the slug appearance which gives you a preview of the YouTube video in the UI.
JASON: Heck yeah, I want to do that. Do I change field type for that?
WHITEP4NTH3R: No, no, we can still use text. You'll see. If you just call that YouTube embed URL, sweet. If you go to "create and configure"
WHITEP4NTH3R: You can make it required if you want, but, yeah, might as well. Because this is important, actually. This is important. If you go to the "appearance" tab, if you click "slug" sorry, "URL," is that will automatically generate you a nice YouTube preview to check you've got the right video in the UI.
WHITEP4NTH3R: So, what else do you want to add?
JASON: Maybe just a little bit of, like, why is this my favorite. So, maybe just a little description field.
WHITEP4NTH3R: You probably don't want to use rich text if we don't have that long. So, I would cancel that one and just use the "long text" option and we can just add some markdown. That's a whole different stream.
JASON: Got it. Okay. So, let's say "why is this your or I guess "why do you want to keep this memory?" You know what? I'm just going to call it "description" so that we can actually, like, pull this out of the field and everything. Okay. So, I'm going to create and configure. We can make this one optional, probably. So, we can, like, leave it optional.
WHITEP4NTH3R: Yeah, yeah.
JASON: Default value. We get the appearance. Markdown. I like that.
WHITEP4NTH3R: Markdown selected for you.
JASON: That's great. Cool.
WHITEP4NTH3R: Cool. And one thing I think we are gonna need, because we are generating dynamic routes, we're gonna have to add a slug field.
WHITEP4NTH3R: Now, there's a really cool feature on this as well. So, if you choose "text," and if we just call this "slug," and then press "create and configure," and we'll make it required on the validation tab, because we definitely need these slugs. If you go to "appearance," and if you click on the slug field and you scroll down a little bit, you can generate a slug from the title.
JASON: Oh, that is handy! And so, okay, yeah, I'm in. I like that a lot.
WHITEP4NTH3R: Okay. And I would also always like to I was gonna do exactly the same thing. It makes so much sense.
JASON: We're the same kind of obsessive! Hooray! (Laughter)
WHITEP4NTH3R: Click "save" on that. Okay. So, now we've got a very nice, basic memory content type. So, let's add a few memories first. So, let's go to the "content" tab.
WHITEP4NTH3R: At the top.
JASON: Oh, right in front of my face.
WHITEP4NTH3R: Yep. And there is "add a memory." I have one memory to add first. I would like to add my favorite memory, which is when I won the Jammy award.
JASON: That was a great I mean, what a I mean, congratulations again.
WHITEP4NTH3R: Thank you.
JASON: Let's see. You are here.
WHITEP4NTH3R: There it is. Look at that. It's as if we planned the rescheduling of the stream to be after the whole thing. But we didn't.
JASON: Okay. So, this is our embed URL.
WHITEP4NTH3R: How did you get to be able to just highlight that on its own? Because whenever I try and copy an embed URL, it copies the whole thing!
JASON: Just lucky, I guess.
WHITEP4NTH3R: Yeah. (Laughter) You'll see that the YouTube video is generated for you in the UI, which is cool.
JASON: All right. So, let me get here.
JASON: Oh, and there's the yep, there's the autoslug. Here's the video already in place.
WHITEP4NTH3R: What a flex. Hello, cryogi. Sweet markdown.
JASON: Do a little markdown so we can see it happen. And publish?
WHITEP4NTH3R: Publish. Let's say let's add three more.
JASON: Okay. So, let's just poke through here. You mentioned the super Supabase. Where's John ?
WHITEP4NTH3R: There it is, down, down a bit.
JASON: Supabase, here it is. Hold on. Let me actually type these through so that people can hear instead of just getting, like, the randomness. [ Music ] Oh, my goodness. Anyways. The whole conference, if you didn't get a chance to join, was about that ridiculous. So, I very much hope that you take some time to watch it because it was so much fun to make.
WHITEP4NTH3R: So, I got I got a slap bracelet coming. A limited edition Jamstack slap bracelet. I cannot wait to slap it on my wrist.
JASON: Let's put this one in. Good.
JASON: Any thoughts on why
WHITEP4NTH3R: I enjoyed this one because of the DJ Khaled, Kanye West, Beyonc�, all the rappers were living it up together. There was a fourth one. JayZ as well. Those four rappers were living it up together as neighbors.
JASON: I love it. All right. So, let's get this one published. And then let's go down here. There were so many good ones here. One that I really enjoyed was Rich Harris' talk about transitional apps. So, if you didn't watch this one, this one is a very good read or a very good listen. I would definitely recommend checking it out. Rich is very smart and has a lot of fun ideas. So, that is a good one to watch. Let's get this one added here. And get the title out. And I'm just gonna that's the hashtag that Rich wants to get trending.
WHITEP4NTH3R: I also liked the RedwoodJS one for the funny hats and how quick the talk descended into chaos throughout.
JASON: All right. So, let's get this one in here.
WHITEP4NTH3R: One more.
JASON: And JamSnacks.
WHITEP4NTH3R: Funny hats.
JASON: Funny hats and chaos. What everyone wants.
WHITEP4NTH3R: Okay, we're ready now. Let's install the GraphQL explorer. Before we do that, let's generate an API key because we're going to need that when we generate the GraphQL explorer. So, when you go to settings and API keys,. So, right now you don't have one, so you can't read your content. You have a space ID, but we need an access token to be able to read the content. These access tokens, the one we're going to use is read only. So, you don't have to worry about making it private because no one can do anything with it apart from reading the data that we're putting in. If you click on the API key in the topright. It's done it for you. In a minute when we install the GraphQL explorer, the content preview API token. If you just copy the content preview API token.
JASON: Got it.
WHITEP4NTH3R: Keep that in your clip board. Let's go and install an app. If you go to the apps menu item and go to "manage apps." A really cool thing about apps that's just come out is app sharing. Now, Contentful let's you extend the UI and extend your editors' experience by building custom apps with an open source framework. We have an NPX create Contentful thing you can use and a whole design system and everything. As of this week, you can now share your apps between organizations rather than having to install a separate installation of it.
JASON: Oh, that's handy.
WHITEP4NTH3R: We're kind of creating this, like, big ecosystem and we're hoping the community is going to be able to share their apps and build loads of really cool stuff to enhance the editor experience. What we're going to install now is the GraphQL explorer. You'll see a little GraphQL icon as you scroll down. There it is. GraphQL playground, and click on that. And click "install." Built by my colleague, Stefan. Just click "authorize access" there, and it will ask you to paste in that key that you just copied. The CPA token.
JASON: This is a public key, right? Or do I need to roll this later?
WHITEP4NTH3R: That's fine. It's read only. It's just going to have access to your preview content. The reason why the GraphQL playground asks for preview content is because you want to be able to query preview content in the explorer rather than just published content.
JASON: So, if I've got, like, drafts and stuff, they'll still show up now? If I put the other token in, it would still work, but only show me my published content?
WHITEP4NTH3R: If you asked for preview content, it wouldn't give it to you in the explorer.
JASON: Got it. Got it.
WHITEP4NTH3R: So, click "install," and then if you refresh the page you have to refresh it for it to show up in the navigation currently. And then if you click on that apps item again, and we can go into the GraphQL explorer. I keep saying explorer because it's kind of the same, isn't it? Right. So, here is a GraphQL playground. And this is the beauty of GraphQL is it's completely selfdocumenting. You've already clicked on on the docs tab already. This is where the schema is generated from the shape of your space. You know with REST when you have to go and read loads of documents to see what's available, what end points do I have available? What am I going to query? What do I get back? GraphQL tells you everything. And what's really cool is if you press controlspace, it gives you hints in there of what you can query.
JASON: I was just gonna show this trick, because this is, like, I live and die by this trick. So, if I want to get I'm assuming a memory collection is how I get all of them, right?
JASON: Okay. So, I'm going to go here, and then you can just open some brackets. Control space. Let's find out what's in the here. Here's some items. That's probably what I want. Let's try it again. Here's our title. Our slug. What else do we want? We want the YouTube embed URL. Do I need anything else? Maybe the description. Okay. I just wrote a query. We're done.
WHITEP4NTH3R: How easy was that, right?
JASON: I didn't have to look at a single page of documentation. Y'all, that's the Superpower. That's why GraphQL is so freaking cool.
WHITEP4NTH3R: Just give me what I want, yo. So, that's the query. Right. So, we're ready now to spin up a new Next.js app and query that.
JASON: Okay. I'm ready. I have my terminal set up and I can create an app.
JASON: Do you have a preferred way of doing it?
WHITEP4NTH3R: I normally do NPX create next app. Jamstack memories. And it's all gonna do it for us. This is how I live my life. I pretty much do this most week most weeks.
WHITEP4NTH3R: New Next.js application. Okay.
JASON: Did it already install wait, where am I?
WHITEP4NTH3R: It does an NPM install for you, yeah.
JASON: Memories. Already did the NPM install. Now we can just straight run this thing.
WHITEP4NTH3R: Yeah, we can. I love seeing everybody's different aliases for NPM run.
JASON: So, I the reason that I use Netlify dev is because it lets me use some of the stuff that I, like, heavily lean on, like Netlify functions in local dev. And if we were using when we start using environment variables, this will also mean we don't have to set up a�.end, we can set it for the
WHITEP4NTH3R: Oh, really?
JASON: And it will pull it in for us.
WHITEP4NTH3R: Oh, I didn't know that. I'm going to try that out. Okay. Cool. So, let's just do everything on our what we're going to do is, first of all, let's get all of our data on the index page. Let's just get all of our data so we can create links to each memory.
JASON: Okay. So, I made a mistake by not opening this first, but now that I've started it, let me start it again.
WHITEP4NTH3R: So, how do you how do you add environment variables now then locally? Would you do it on the command line via the Netlify CLI?
JASON: Yeah, so, we can. There's a command we can run maybe I'll just keep this open. Go here and here. And memories. So, if I run, like, env:set, and then I can say SOME, it will try to set it. I haven't actually hooked this up to a site yet, so when we deploy it, it will actually attach it to that site and get a twoway communication between it. There is cool stuff I can do. I can run it and make it available live. People can see my local instance for sharing quickly and getting feedback and stuff. We don't talk about the Netlify CLI and I don't know why we don't talk about it. We're trying to fix that, because it does so much stuff and I just, like, no one knows about it.
WHITEP4NTH3R: I am going I am going on this train. Don't worry. Don't you worry. Okay. So, we are gonna need two environment variables in order to query the data anyway. I don't know whether you want to hard code them right now or add a project to Netlify and add them like that.
JASON: How about this, why don't we just deploy this site and we can just deploy it as we go? So, I'm using Netlify CLI. I'm going to Netlify init. I net to connect to GitHub first. I did a gitinit. This is a pretty standard, like, default thing. All right. So, let me get commit. And we'll say "initial site skillington." Okay. Then I'm going to use the GitHub CLI.
WHITEP4NTH3R: Just laughing at what Cassidy said. This is my sick voice, Cassidy. You don't want this voice in your ear all day, every day. Maybe you do. I don't know.
JASON: Okay. So, I used the GitHub CLI to create a new repo called Jamstack Memories.
WHITEP4NTH3R: I love the GitHub CLI as well. That's all I use and it's just so nice. CLI is the way forward. CLI is the future.
JASON: It really I can't even process how much it makes a giant difference for me. Because check this out. We now have that's all set up, like, this exists. So, now I can Netlify init. I'm going to create and configure a new site.
WHITEP4NTH3R: This is magic, isn't it? This is the magic of the world we live in right now.
JASON: And it's auto detecting what are commands are, so we're gonna use Next build, we're gonna set the directory to out. Use the essential Next.js plugin which lets us run the Next API routes and such. It's going to set up our Netlify.toml for us. That's done. And now we need to set some environment variables. So, I need the is it the deploy preview key?
WHITEP4NTH3R: So, you need the space ID, first of all,.
JASON: Space ID. Is there, like, a format I need to use for these?
WHITEP4NTH3R: No. Sorry, you'll find it in your Contentful space. It's just a 12character thingy. There you go.
JASON: It's this one, right?
JASON: Okay. And it doesn't matter what I call it?
JASON: Code's not gonna look for it.
WHITEP4NTH3R: We'll put it in the code, yeah. Then the access token. Now, this is the delivery token. You don't want the preview token.
JASON: Delivery token. And this is another read only one, right?
WHITEP4NTH3R: Yes. If you just get that from the settings API keys area.
JASON: Add API key. We're going to create a deliver token.
WHITEP4NTH3R: Yeah, that's the one you want.
JASON: Okay. So, back over here we go.
WHITEP4NTH3R: You've lost your query now, but never mind. It was so quick to make, you can just make it again.
JASON: Oh, no. Okay. So, we'll go back. Let's see if it remembers me. No. Okay. That's fine.
WHITEP4NTH3R: That's fine.
JASON: We'll just do it again. So, we'll get our memory.
WHITEP4NTH3R: I make that mistake all the time. Because I presume it's so nice, I presume that it should have saved it for me. But nope. Do it all the time.
JASON: Oh, wait, I need my items. And then I'll get my title. Slug. YouTube. And description. Okay. Just run that to make sure it works. There it is. All good.
JASON: Now I've got that.
WHITEP4NTH3R: You've got that. So, on the index, first of all, on the index on the homepage, we are going to use get static props just to fetch all of this data so we can create clientside links using Next Link to each different route.
JASON: Okay. What's your preference, top or bottom of the file for these?
WHITEP4NTH3R: Wow, I've never done it at the top. Should I change my life and do it at the top?
JASON: I mean, I will never judge somebody for how they choose to organize their files.
WHITEP4NTH3R: I sense a "but."
JASON: No, not at all. I have no judgement for the way that people organize files. I put mine at the top only because I put stuff in the order that it gets interpreted so that I, like, as I'm scanning down a page, I see all the stuff that's going to happen and then I see it get used, instead of, like, where did this function come from? Oh, it's at the bottom. Where did this function come from? Oh, it's at the bottom.
WHITEP4NTH3R: It's very interesting, I like to so, the way I visualize it, you've got all the front end components. Here is my front end, right? Then here's all the stuff I need for it that I'm going to throw from the bottom like a basketball hoop into the component.
JASON: Oh, yeah.
WHITEP4NTH3R: I think of it like that. I don't know why. Anyway, let's just do what I how I always write this is I always return props. And then decide on the data I'm gonna return at the bottom of that function. And then so, we're gonna return memories. We're just gonna return memories, which is gonna be an array. And then I construct what my and then, yeah, normally, I will put the memories into the top of the component and then check it still runs with an empty array, and then I generate my memories, getting them from the data.
JASON: Okay. And so because we because we returned a prop called "memories," we get a propped called "memories" here, that's the magic of the next get static props. So, then let's do a little bit of editing here. We'll just get rid of all of this.
WHITEP4NTH3R: I wrote a batch script to reset a Next app that clears all this up. It's on my website.
JASON: Oh, nice.
WHITEP4NTH3R: I was sick of doing it all the time. It's useful to have.
JASON: I'm going to use a favorite debugging trick, which is to just stringify stuff. Okay. We've got that. I'm just gonna simplify this down as much as I can. So, we've got okay, I'll update this, I guess. Okay. So, that should work.
WHITEP4NTH3R: So, that should now run. Yep.
JASON: And now check this out. When I run this, because I'm using Netlify Dev, there are my environment variables. So, we now have access to those. And then if I if I go to Netlify as well, I will have my let's see if I click through to it. It's here. Opened in the wrong tab. So, let me pull that over. It's also now in my site settings. Under the "environment." So, it, like, lets you do all of this work that would have required clicking around and UI stuff.
JASON: I don't have to set up my�.env package and those things. Things just work and that's very convenient for me.
WHITEP4NTH3R: Beautiful. Beautiful.
JASON: What is error? Socket hang up. Oh, there it is. Okay.
WHITEP4NTH3R: Jamstack memories. Empty array.
JASON: Tada! And if we turn this into something, like, we have our
WHITEP4NTH3R: 1, 2, 3.
JASON: We do something like that. And there it is. So, this is good debugging. I use this
WHITEP4NTH3R: You know, I need to do more of this, because I normally just put it out into the terminal console. When I used to build apps years and years and years ago, I always used to use a pretag to build stuff when I built stuff with Angular. I need to go back to this. It's so good. Also show people what you're doing rather than having to go to a terminal and stuff. So, I've learned with Jason. Love it. (Laughter)
JASON: Okay. So, do we have access to I need to install, like, Node Fetch or something, right?
WHITEP4NTH3R: Nope. Next.js just poly fills it all for you. You can just use async, await whatever you want.
JASON: Okay. So, I need to async this function. If I can spell it correctly.
JASON: Then we will await, fetch
WHITEP4NTH3R: Yeah. The URL
JASON: I need to know what my URL is.
WHITEP4NTH3R: So, you'll find that in the GraphQL playground.
WHITEP4NTH3R: And I would usually replace the space ID with your environment variable there.
JASON: Contentful space ID.
WHITEP4NTH3R: And then you need to send some headers in. Just two little headers. One of them is authorization.
JASON: Is this like a bearer?
WHITEP4NTH3R: Yep, bearer. And then the access token.
JASON: I got to look this one up. I forgot what I called it.
WHITEP4NTH3R: Contentful delivery token, you called it.
WHITEP4NTH3R: And content type, which is application JSON.
JASON: That was a very French pronunciation.
JASON: Application JSON.
WHITEP4NTH3R: You are testing me here, because I haven't got this up in front of me, and I think I can do this. After the headers, we want the body. We're going to JSON and stringify the query that you defined below. And then you're good to go.
WHITEP4NTH3R: Then you need to response.json the response. (Laughter) JSON.
JASON: Okay. So, we've got that. And then I always do just, like, a little baby check here.
WHITEP4NTH3R: Mmhmm. I'm just gonna wait for Cassidy's iterations of JSON as you do this.
JASON: Okay. So, if we do it that way, we're gonna get back data memory collection, and I want data.memory.collection.items.
WHITEP4NTH3R: You want the items, yes.
JASON: Okay. So, we're going to be able to say result oh, wait, no. I need I need more. Await result.json and then the memories will be data.memoryCollection.items. Okay. So, now that we've got those, we can put these in instead of a hard coded array. And theoretically speaking
WHITEP4NTH3R: It works first time. Because I got my headers right. Oh, you need to do post you need to do method post. Dang in! We were so close.
JASON: So close. I forgot that one, too.
WHITEP4NTH3R: You got to post to GraphQL
JASON: By the way, for everybody who's curious, this is GraphQL without GraphQL client. So, we're sending this the same way you would send a REST call, without any additional libraries or anything. And that's a very handy way
WHITEP4NTH3R: I love that, too, about GraphQL, because you don't have to install any dependencies. Anyone who knows me knows I hate installing dependencies. Look, we did it!
JASON: We got it!. Success.
JASON: That's it, right? We're done. This is the whole website.
WHITEP4NTH3R: Done. See ya later. So meta. Right. So, what I'd like to do on this homepage is loop through the memories just with Map or something. And then we want to import Next Link at the top of the file. And I want to create a clientside route to these the slugs of each memory.
JASON: Okay. So, we can set up let's just do it as an unordered list because those are fast to set up.
JASON: And for each of these, we're gonna do an li and we'll give it a key of, like, memory.slug.
WHITEP4NTH3R: That's fine. I was gonna say get the ID, but we can do that. That's fine.
JASON: Yeah, I if we decide we care, we can fix it.
WHITEP4NTH3R: That's fine.
JASON: And then we wanted a link. Can I auto import? No. Let's get import link from next link.
WHITEP4NTH3R: So, for those of you that don't know, link from next link enables a clientside route. It's not a browser full redirect request. That's what makes clientside routing really speedy and it feels like you don't ever leave the page. That's what we want. Because we're going to a route that exists in the app.
JASON: Mmhmm. How do you want to structure this?
WHITEP4NTH3R: I mean, it doesn't really matter. We can just go straight to the slug.
JASON: Let's do it.
WHITEP4NTH3R: Right now. Can't we?
JASON: Memory.slug. And then
JASON: Title. Okay. So, that'll do it, I think.
WHITEP4NTH3R: That'll do it. That'll do it.
JASON: We don't need this anymore. Oh, HF. I did I've been writing too much there we go.
WHITEP4NTH3R: Sweet. Then we have our links.
JASON: Not very linky, but they are actually links. But when I click through, nothing.
WHITEP4NTH3R: Got 404. Let's fix the 404s! So, what we want to do, inside the "pages" folder, we want to create a file called "[�memory.js." This is a dynamic route. You can also nest them as well inside different folders such as /blog /post /memories, whatever you want. And the next function we're going to write is creating dynamic routes with Next.js. We're going to export an async function called getStaticPaths.
JASON: I got ahead of you.
WHITEP4NTH3R: You did. This function is what's gonna create all of the different routes dynamically based on the data that we provided. So, normally, because you're using the same query that we're gonna well, we're not even gonna use the same query. You can now create a GraphQL query that just gets the slugs of the memories. And not all the other stuff.
JASON: Okay. So, let me grab all of this. And so if we were writing a more productionready app, we'd probably abstract this out a little bit. But, instead, we're just going to copy pasta, because that's how you get things done. So, we now have just a slug. And so
JASON: I can call this "memory slugs" instead. That sounds like something you would read about in a human comic book. Oh, we got to battle the memory slugs. (Laughter)
WHITEP4NTH3R: Quick, get out the JSON. Right. We're gonna return from this. We returned some slightly different things. Here, we're gonna return an array of paths. So, paths array. And the format of this array so, we're gonna probably create this above this is an object with key params which is an object which will be the which has we're gonna have slug is the value.
JASON: Got it. Okay. So, let's make this happen. We can paths will be memorySlugs.map and we'll get a slug. And then
WHITEP4NTH3R: We got a little raid. Thank you for the raid.
JASON: Oh, yeah. What's going on with my volume over here? We don't have any sound. Let me restart the overlay. Because that's unacceptable to not have any audio.
WHITEP4NTH3R: It's the worst, isn't it? I hate it when that happens.
JASON: Yeah, let's see thank you for the raid, Jacob. Did somebody I saw subs and all sorts of stuff. So, thank you. I think Cassidy just hit 11 months. So, almost a year. So, thank you for that. I saw somebody else sub earlier and I'm sorry I didn't say anything. Do we have noise now? No. Oh, there it is. Okay. We got noise. Good, good. We have we have volume back. So, if anybody wants to make noise, you can do that.
WHITEP4NTH3R: Peter has asked why we're using get static path? It is because we are generating dynamic routes based on the data via the GraphQL API. And then we will use get static props to get the data for each post by slug, where we'll write another GraphQL query, where we'll query one post by its own slug. And we also want to return with the at the bottom of this function a fall back value. Now, I'll just explain what the fallback value means. The fallback can either be true, false, or blocking. Now, fallback true so, remember when I talked about at the beginning when you had thousands and thousands of pages and you only wanted to generate your top ten dynamic routes at build time? If you do fallback true, on the component of the page above this function, you would use Next router to detect whether it's a in a fallback state or not. You can do a skeleton load. Next will automatically generate that for you statically on the server without having to generate it at build time and take up your build resources. If you use false, it just returns a 404.
WHITEP4NTH3R: If it doesn't exist. And then the blocking does something else different, which I can't remember right now.
JASON: I think
WHITEP4NTH3R: Fallback blocking.
JASON: Instead of the skeleton, it would wait until the render is done and then it would
WHITEP4NTH3R: Yes. So, you either see nothing with blocking. You see a 404 if it doesn't exist already with fallback false. And with fallback true, you can show loading animations and things like that. So I think for this one, we're going to generate them all. We can say fallback: false. Okay. That should generate all of our dynamic routes. So, technically, right now if you run a Next build command, you would see in the Next folder all of the routes being generated.
JASON: Let's do it.
WHITEP4NTH3R: From if the code works.
JASON: Yeah, I mean that's a big if.
WHITEP4NTH3R: I always like to show this when I show this because you can't really get, like, the full appreciation of what it's doing at build time. It did break.
JASON: I broke it.
WHITEP4NTH3R: Oh, did you pass in memory? Go down. Go down a bit. Yes, because we called the file you need to yeah, because we called the file "memory," the param needs to be "memory." There you go.
JASON: Okay. Got it. Let's try that again.
WHITEP4NTH3R: I make that mistake all the time as well. It is a slug but we called the file "memory."
JASON: No. Okay. Is there, like, an actual error here? Props must be returned as a plain object.
WHITEP4NTH3R: It's because we're not returning any props. Let's do that build in a minute. We need to return get static props.
JASON: Okay. So, export async function. GetStaticProps. We can return props as nothing and then try it and it should work. Gonna do it. Do the thing.
WHITEP4NTH3R: Do the thing.
JASON: No. Still not doing the thing, but it's doing some things. Collecting page data. It failed. Size 0. Body 0. What are you?
WHITEP4NTH3R: 401, unauthorized.
JASON: Oh, it's because I oh, it's because I didn't use the that's just
WHITEP4NTH3R: Very good.
JASON: So, now it's gonna build.
WHITEP4NTH3R: But will it build locally now. It will build on Netlify, won't it?
JASON: It's gonna build locally. This is a simulation of our build bot, which is a good way to test, like, how everything's gonna run when you deploy it. So, this is, like if you've looked at a Netlify deploy bot, this is going to look really familiar.
WHITEP4NTH3R: Site is live. I love seeing that at the end. Site is live.
JASON: Okay. Success. All done. And it built out the routes. And this is some stuff that we have to do at Netlify to make the dynamic rendering work. So, we just kind of deploy it as serverless functions.
WHITEP4NTH3R: If you go into your�.Next folder, you'll see the different HTML pages that have been built from the data we got from the GraphQL API. It's so satisfying to see that happen.
JASON: That is extremely satisfying. I'm trying to get this to wrap so that we can no, I just broke it all. Never mind.
WHITEP4NTH3R: Never mind. Anyway, so, you can see the files.
JASON: Yeah, you can see that they're there. They have this TODO content, right? And then if we run this, we'll be able to actually visit the pages, but they'll be TODOs.
WHITEP4NTH3R: TODO, TODO, TODO.
JASON: I don't know why this just happened, but in my head I went "baby shark"
WHITEP4NTH3R: Everyone knows how much I hate that song. Please stop. I hate you now. I'm leaving. Bye. (Laughter)
JASON: Okay. So, here we go. We got it. We got this thing, like, functional at least. The URLs work and I can click into any of these. And the slug is there. Good, good. Okay. So, then the next thing would be to get some data.
WHITEP4NTH3R: The next thing is in get static props, we receive a params object which comes in destructured like that. Yep. We will have available inside this function params.memory in this case, which is gonna be weird because it's actually the slug we're querying by, but not to worry. And so what we want to do now is go back to the GraphQL explorer and, and let's construct a query where we are searching for one memory by its slug.
WHITEP4NTH3R: So, what we really need to do is just on line 2, we can add aware add a new one. Okay. Fine, fine. Love it.
JASON: And then we can go with "memory," and then we can get the title slug, YouTube, and description. A nice thing about this explorer is it lets you choose the one you want to run. So, we can get that, but there is no ID, right? So, that means we need to pass in an ID or slug?
WHITEP4NTH3R: We're going to be querying it by the slug. So, you just do where slug equals whatever. Although, I've never done it like this. So, you're going rogue, but that's fine.
JASON: Oh, maybe you can't.
WHITEP4NTH3R: I query I think you have to query a collection where the slug is.
JASON: Okay, I get it. Well, then, I'm going to copy it like this and we're going to do it the way you were telling me to do it and I'm just gonna listen. Where slug
WHITEP4NTH3R: And then if you find a slug from the front end, then we'll just test that one of them works. And then we can just use that query again.
JASON: There we go. Okay.
WHITEP4NTH3R: Sweet. Okay. So, let's copy that query. And then we'll put that into getStaticProps with an interpolated string in the query, which is memory. And, again, we have to copy and paste all that Fetch. That's okay. We're building first and engineering later.
JASON: Right. I like that. That's good phrasing. Okay. So, we're going to go with this. All right. And then I will show you a trick that I like. Which is
JASON: to take this here. And then I can drop this in. Okay. Now, what I want to do is I actually just want to use GraphQL variables so we can do slug.
WHITEP4NTH3R: Ooh, yes.
JASON: And the slug type we'll look up real quick is a string. And then
WHITEP4NTH3R: The bang means it's required, isn't it?
WHITEP4NTH3R: As well.
JASON: So, this is saying that you have to provide a slug. A slug is a type of string. If you don't provide one, it'll have an error. Then we get to use this here. And then below, I can provide slug of memory.
WHITEP4NTH3R: Beautiful. This is more secure. I only actually just recently learned about this in the last couple of months. I've got a big branch on my blog where I'm refactoring all my GraphQL queries to reuse these variables properly, to sanitize my queries. A blog post is coming up about this, by the way. A big work in progress. It's a huge project to refactor, but I'm glad you brought this up.
JASON: Okay. So, I'm gonna do memory data. And that's gonna be data.memoryCollection.items.
WHITEP4NTH3R: You might want to put a limit 1 as well, just to show the what you would normally do you only want one memory, don't you? And where you find there might be a case where you have identical slugs, but we wouldn't.
JASON: I mean, we hope not, but
WHITEP4NTH3R: We hope not, but you never know.
JASON: Any time you say it definitely won't happen, it definitely will. So, I run it like that. Does what we want. Okay. So, there's our limit of 1. And the formatting on these doesn't matter. So, we can make this a little bit less unpleasant to look at. Now we can kind of see what's going on. So, we got a limit of 1. I destructured down to memory data. So, then I'm going to just say "memory: memory data." So, then up here, we should get the memory, and we can do everybody's favorite debugging trick.
WHITEP4NTH3R: And this is great because we're building four pages at once with just with just one block of code. And it's just I still find it magic. This is why I love doing what I do, because it's just look at that.
JASON: Beauty. All right.
WHITEP4NTH3R: All right.
WHITEP4NTH3R: Let's go to video.
JASON: Here's the cool part, right? So, here's the one we're at. Click through. Let's go to single page apps here on the web. There it is. Okay. Now we've got another one back through. Like all the data's coming in. We had, as you said, like, we didn't write a lot of code here. This is largely boilerplate. So, it's, like, get this query, pull out the variable, and then just return the thing. It's not a ton of code outside of this boilerplate for sending a request. That feels really powerful. That's nice.
WHITEP4NTH3R: And usually, like you were talking about, if you'd abstract that out, it would just be three lines of code to create that whole page from that query, you know, you've moved the query somewhere else. It would just look so nice and clean and not scary as well. This is why this is great for beginners. Because if you just put your fetch function in one place, just reuse it all the time and pass in a query, it's just so nice. It's so nice.
JASON: Alan is asking in the chat if you understand correctly there are a few thousand pages on your site. You can build on Netlify without having to generate them all at build time. Yes. So, Next is set up where you can send it any number of static paths. So, like, you know, you could use that limit that we just saw to set it to say the first 500 pages or the first 100 pages or whatever. And then you could use that fallback true to run to basically load the data at run time. And that would be rendered in a serverless function on Netlify. So, that is how that works.
WHITEP4NTH3R: And in a largescale business in a largescale business replication, you'd normally have your Data team working on the top pages that people visit, and these are the ones that you should prioritize. You probably get those from a distance somewhere to build those top pages, which might change every build as well. You can do some really clever stuff, I think, with optimizing the experience for speed based on how your visitors use your site.
JASON: Mmhmm. Yeah. So, why don't we actually get this thing to do stuff. So, now that we've got data, I'm gonna collapse these down. And we have let's see. I set up lining like a div? Did I just reuse the container out of this one? I'm just gonna do that thing.
WHITEP4NTH3R: You need your next head as well. For that sweet, sweet, SEO stuff.
JASON: Oh, yeah. Good, good. And we probably need a link to home as well. So, why don't we just bring all of this over?
WHITEP4NTH3R: Yes. Good idea.
JASON: Okay. So, then we're going to wrap this all in a div with a class of container.
WHITEP4NTH3R: Alan's asking, does that also mean it's possible to update the content without redeploying the site? Yes. So, you can use Contentful to trigger web hooks to rebuild sites on Netlify, et cetera, when you change content, but you also don't have to do that.
WHITEP4NTH3R: And you can run periodic builds, if you want, via automation. Or anything like that. Or you can even use one of my favorite things, which is incremental static regeneration. Which on a page level, if you use the revalidate parameter, you can choose Next to validate the staleness of your data at certain periods after users visit your site. And if the data is changed, Next in the background will rebuild that page only and not the whole site. Again, more environmentally friendly, rather than building the whole site every time someone changes an entry and you're building thousands and thousands of pages. So flexible. So beautiful. I love seeing it in action. It still makes me cry with joy.
JASON: Okay. And then for the for this part, we're not gonna parse that markdown because I don't think we have enough time to set that unless you've got a fast version.
WHITEP4NTH3R: I mean, no React markdown is what I would have suggested. So, we'll just go with. We will be meta dev cool. Everyone can read markdown in this chat.
JASON: So, we'll set up the title to be memory.title. And then
WHITEP4NTH3R: The description can be in the description as well.
JASON: Oh, yeah, yeah, we can definitely do that. So, it's meta name is it name description?
WHITEP4NTH3R: Yes. Content equals the thing.
JASON: Memory description. Oops. Okay. And then finally, down here, we will add a link and we'll just send everybody back home and say okay.
WHITEP4NTH3R: HTML entities. Look at that.
JASON: I know. I feel like this is one of those things that, like, dates me as a developer, is I have some of the HTML entities memorized.
WHITEP4NTH3R: That's one of my favorites, actually. I do enjoy that arrow.
WHITEP4NTH3R: Look at that. [ Music ]
WHITEP4NTH3R: We're watching it, are we?
Hi, welcome back. I hope you are enjoying the conference and having fun.
JASON: Oh, Kenny.
Anyways, it's time to announce our next winner for the community creator category.
JASON: We're just gonna relive this whole moment.
All nominated and voted for creating amazing Jamstack content for the community. That's pretty much it. They are doing incredible work putting out content, resources, blog posts, video tutorials, Twitch streams, you name it. And the community appreciates them for doing all of this and making content that we all learn from. Without further ado, the six I think it's five. The top five finalists, who made it to this category are Stephanie, Salma AlamNaylor, and Benedict Ray. Thank you all
JASON: That is a formidable crew to be up against.
WHITEP4NTH3R: I know.
The person with the highest number of votes in this category is: [�Drum roll�] Wait for it. Is Salma AlamNaylor.
WHITEP4NTH3R: Oh, my gosh. So silly.
JASON: Can we what did we decide it was? Can we get a "B" in the chat for ballin? (Laughter)
WHITEP4NTH3R: Speech. "B" in chat.
JASON: Look, y'all, we did it. Like, this is this is an app being fully rendered. We had to write surprisingly little code for how much this allows us to do. Then if we add more. Let's just add another one. Cassie's the best.
WHITEP4NTH3R: Let's add another one.
JASON: We are going to come over to content. And we're going to add a memory. There is Cassie. We'll share. Get the embed URL.
WHITEP4NTH3R: Cassie is the best.
JASON: And then
WHITEP4NTH3R: I thought you said Cassidy as well. And Cassidy's ears perked up.
JASON: No, Cassidy is the worst. Cassie is the best. (Laughter) But, look, it did it.
WHITEP4NTH3R: Look at that!
JASON: We didn't have to write any more code. So, this is really flexible. This is really nice. Like I wonderful. But, you know, like, this is a really, really nice way to work. Because it's one of those things where you set up the structure, and then the machine continues to work. You don't have to
WHITEP4NTH3R: Mmhmm. S you know, the Content Team doesn't have to ping the web team every time something changes. The content team is now free. They edit things on Contentful and the site just works.
WHITEP4NTH3R: Mmhmm. It's glorious.
JASON: Should we let's let's deploy this.
WHITEP4NTH3R: I think you need to add in the header some kind of you need to add, like, some branding. Maybe, like, the slap bracelet or something like that.
JASON: The slap bracelet.
WHITEP4NTH3R: I can't wait to get my slap bracelet.
JASON: I am really excited about that slap bracelet. Let me pull up I think I have the file here somewhere where I can just grab it.
JASON: Here is the I'll just pull this over. This is our this is our massive Figma file. You can see all the work that went into this. So many assets got created. Hello? You can do it.
WHITEP4NTH3R: I want to live in Jamstack Conf world. I want to live in that space, time continuum.
JASON: So much fun with this. All right. Let me export one of these.
WHITEP4NTH3R: You've got the actual bracelet.
JASON: Yeah, this is the bracelet you can get. It's an actual slap bracelet.
WHITEP4NTH3R: Who even makes them? How did you find a supplier for slap bracelets?
JASON: So, here's the thing. We work with someone named Ashlyn. And Ashlyn is, like, you know when you watch movies about, like, the Mafia or, like, gangland bosses and there is always, like, the boss and then there is that person who is just their fixer? Like no matter what you need, yeah, got it. I'm on it. Ashlyn is a fixer. So, we just made a joke and said, wouldn't it be really funny if our swag was a slap bracelet. Two hours later, she's like I got prices and a supplier. Let's make this happen. Winston Wolf in real life. Winston Wolf from that's "Pulp Fiction," right? It is magic to have a Ashlyn in your life. I recommend everyone gets an Ashlyn. Let's stick this up at the top. Which means we need a common layout. Which I'm gonna do that in the app, is that right?
WHITEP4NTH3R: No, you wouldn't want to do it in the app, but you could do it in the app.
JASON: I guess, theoretically, I can do whatever I want.
WHITEP4NTH3R: Maybe create a component create a component that is the header, which is the SVG or something and then you can add that component to both pages.
JASON: Okay. So, I'm gonna create a new file. Component header. And in here. Hello? Uhoh. We've locked up, everyone.
WHITEP4NTH3R: Oh, what happened? What did you do? The slap bracelet is too powerful.
JASON: I shouldn't have opened Fimga. Maybe this will help. I closed Fimga.
WHITEP4NTH3R: Too much memory.
JASON: Come on. Maybe I should have reopened it. Oh, no.
WHITEP4NTH3R: This is tense.
JASON: Okay. Let's reopen. Okay. Back to my component header. We're going to export default component header nope. Function header. And it is going to return, we'll do a header, and inside the header, we can have an image with the source of where do I put this?
JASON: And let's rename that to something a little more web friendly. Okay. And so I'm going to just go right to slapbracelet.
WHITEP4NTH3R: Jamstack Conf slap bracelet.
JASON: All right. And that'll be that kind of works. So, let's bring that in.
WHITEP4NTH3R: I see you also have a special order for your imports.
JASON: I yes, I do.
WHITEP4NTH3R: Me too.
JASON: I can't I can't not do it. (Laughter) Okay. So, we can put that in here. And then if I copy let's see copy here.
WHITEP4NTH3R: Fun fact on HTML. Jason put the header outside of the main, which is correct, because the content inside the main shouldn't be repeated across pages, and the header is going to be repeated across pages.
JASON: And I just realized we should have
WHITEP4NTH3R: Have a main.
JASON: wrapped this all in a main. I got confused by that div wrapper. Okay. So, if we do that and that, that should give us
JASON: Tada! Oh, but what happened here?
WHITEP4NTH3R: It's because one of them is in a div and one of them is not in a div. So, if you wrap it in a fragment rather than a div, you should be fine. Or wrap the other one in a div. It's the way your index compared to your memory pages are different, I believe.
WHITEP4NTH3R: Oh, you got class equals container on that one and not the other one.
JASON: And this one.
WHITEP4NTH3R: No, you have got it. Interesting.
JASON: Why are you different? Oh, is it because it looked like it was gritty. Let's see what happens here. So, I've got this one. I've got that one.
WHITEP4NTH3R: Oh, do you know what it is? There's a flex thing on the main container thing that stretches it out in the CSS. Take that styles.main off and you'll be fine.
JASON: Or add it to both.
WHITEP4NTH3R: Just something that comes default with Next.js.
JASON: It's frozen on me. Come on now.
WHITEP4NTH3R: Too much power.
JASON: We overdid it. The slap bracelet was one step too far. Okay. There we go. So, now we've got a nice consistent feel. We've got we've got our Jamstack Memories. We've got that. And I think we can just go ahead and deploy this thing.
WHITEP4NTH3R: Let's deploy it on the CLI. Look at that. In, like, an hour, we've set up everything in Contentful. We've added some entries. We've just written a few GraphQL queries, a few lines of code, and we've got a whole website that actually is meaningful and it's not just, like, a test.
WHITEP4NTH3R: It's real. Real memories.
JASON: So, I just ran the Netlify open command, which will pop this open in the browser, so we can watch this thing build. So, we pushed to GitHub, and that's what kicked off the build. We don't actually this is just so we can watch. We don't even need to watch. It'll just happen. But the site is currently let's open this up. Here. So, this is the, like, the site that's been published. So, as it's building, we'll see all this stuff happen. Here it goes.
WHITEP4NTH3R: Generating static pages.
JASON: Yep. Packaging up.
WHITEP4NTH3R: I love to watch logs to comfort me. I like to see what's happening. And I like to see all of my hard work come to fruition. And my pipelines do what they need. I love it.
WHITEP4NTH3R: Logs can be a scary thing when you're first starting out. Logs can be scary because there's so many numbers and letters and all sorts of nonsense. Good to get used to.
JASON: All nonsense. Yeah. I mean, it really is great to kind of just be able to see the robots do the work. Like, you know, too much of it feels a little, oh, let's go into a dark room and see what happens. But now that's done. And here are the sparkles. Your site is now live.
WHITEP4NTH3R: Site is live!
JASON: And if we reload this tada!
WHITEP4NTH3R: Oh, "B" in chat!
JASON: And you can go visit that right now. We did it. Put that "B" in chat.
WHITEP4NTH3R: Look at that.
JASON: Yeah, I mean, this is that's very nice. Very nice. Okay. So, I feel like this is a success. We set out to model some content in Contentful, use the GraphQL API, store some of our favorite memories from Jamstack Conf, which if you haven't seen Jamstack Conf yet, actually, you know what? I'm not gonna show you that. I'm gonna show you this because this is I'm so proud of what we were able to do here. Where is it? Where is it? We got to make this video. Okay. Everybody go look at this. And I'm gonna make you watch this right now. Dance with somebody� I wanna dance with somebody� I wanna feel the heat�
Oh, nothing. Have you uploaded the Jamstack Conf videos? That's what I was doing. They're uploaded. That means you can go watch them right now. You should do that.
JASON: Anyways, go watch those videos. Go check that out. Pause that so you don't have to watch it on repeat. So, where should people go if they want to go further here? What are next steps?
WHITEP4NTH3R: If you want to learn more about Contentful, you can go to Contentful.com/developers. Which is our developer portal. We've got videos, link to documentation. And we're actually expanding out this in the next few months to really showcase what the community is building with Contentful as well. And to show more and more starters and more and more example projects. Because the beauty of Contentful is that it's completely language and framework agnostic. Anything that can make an HTTP connection can use Contentful. So, go and build stuff with Contentful for your smart fridges and watches and billboards and buses and everything. It's not just for websites. Which is cool. Join our Slack as well. If you use Contentful and have any problems, you can go to Contentful.com/slack. And go and have a play with Next.js. And the docs, I love the Next.js docs. Docs are a really hard thing to get right, and I always have a good time on the Next.js docs, because you can search for exactly what you're looking for. And it's a really nice search. It's a really nice experience. They take you through a little story. There is a really good base tutorial of getting started on the Next.js docs as well. And they also provide a Next.js starter for the Contentful CMS. I've also got a Next.js starter which is slightly more engineered than the Next.js one as well. If you wanted something like that. And just come and watch my streams and watch me build stuff and learn things with Next.js and Contentful as well.
JASON: Yes. Another shoutout. Make sure you go and, you know, smash that subscribe button. Ring that Bell. Is this the one you're talking about?
WHITEP4NTH3R: Yes, that's the one.
JASON: All right, everybody. Why don't you go ahead one more time, give us a "B" in chat. Thank you so much for hanging out with us. I am going to do a quick run through of all the things that are good about this stream. Thanks to other people. So, first and foremost, Jordan has been here all day doing the live captioning here from White Coat Captioning, which is very, very helpful and means a lot. That's made possible through the support of our sponsors, Netlify, Fauna, Auth0, and for the last time, Hasura. So, thank you all so much for kicking in to make this show a little bit more accessible. It means a lot to me. Just one more reminder, if your company is looking to sponsor streams, maybe give me a little call. Okay. That is about it for today. Make sure you please go and look at the schedule. There are so, so many amazing people coming up very soon to do incredible things. Just go scroll through this and be amazed at how awesome these people are and how lucky I am to get to learn from them. whitep4nth3r, thank you so much for hanging out with us today.
WHITEP4NTH3R: Thank you, Jason.
JASON: Any parting words before we call this done?
WHITEP4NTH3R: Build stuff, learn things, love what you do, and have a great day.
JASON: All right, y'all, we're going to go find somebody to raid. Thank you so much. We will see you next time.
Closed captioning and more are made possible by our sponsors: