Next.js and Contentful with Lee Robinson from Vercel

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Applause] [Music] hello hello lee how are you doing hey hey hey i'm doing well thanks for having me join this live stream i am so excited to have you here it's been informally a contentful march has been next js month for us we have a bunch of great live streams that we did about an xjs we did something about the cool new uh static rebuilding that you all have launched recently and we have a bunch of fun blog posts and starters that have been going up all month long and so i'm so excited that we get to wrap up next js month by actually having someone who who works on next chance to join us on stream to teach us so it's great to have you yeah i've been i've been following the content y'all been putting out it's been great so i'm very excited to join and talk a little bit more about nextgs yeah it's going to be a lot of fun for folks watching it's uh it's always a delight to have you we we love reading the comments we love seeing that you're here so do say hi uh and we've got some we've got some fun stuff i think to motivate people to talk this time and i'll i'll be sharing that in a little bit so okay first off uh i'm gonna do the quick run through of my of my slides and stuff and then we can we can talk about the links that you have to share lee and then we can talk about our swag giveaway that we're gonna be doing this stream so we're gonna be giving away swag um as well which should be a lot of fun and something new that we're trying so um first off as always we do all try to keep all of our contentful stuff under the contempo code of conduct so like the tl dr is that harassment and abuse are not tolerated in any contentful communities if someone is doing something to make you feel uncomfortable please do reach out we we have a full moderation list upon our documentation and a full cut of conduct that you can check out uh at any time um we also have a really great developer portal that you can check out to learn and get started with contentful and and pick things up and and learn uh all sorts of fun stuff you can also join our slack there which is the best place to go to ask us questions and then we have our graphql course which will teach you react and graphql uh and then if you build something with graphql we'll hook you up with some swag too so lee uh you've got some links as well i i know i'm gonna let you have the floor to plug though yeah my links are are pretty brief uh follow me on twitter if you want to hear more about what's coming out with nextgs and new projects that we're working on uh new examples and i can also answer any of your questions there as well too and if you are just getting the next gesture you want to learn more check out the official learn tutorial which will walk you through building your own blog from scratch yeah that's awesome all right so we uh the last thing i want to talk about is our brand new giveaway so this is salma has made this really phenomenal giveaway bot that i'm really excited about and so how it works is i'm going to go ahead and click the start button and then click the announce button and hopefully something should show up in the twitch chat alright here we go so we have a prize drawing uh all you have to do to win some contentful swag so some socks and uh and some stickers and all sorts of goodies is you just have to type uh exclamation mark win into the channel um and that'll uh that'll enter you in for our drawing at the end of the stream i will pull a name out of the hat and that person will get some swag and it should be a lot of fun i'm really excited about it i really want to show a picture of the drawing bot but i don't want to reveal the url as well so uh you're gonna have to take my word for it that it's pretty cool so yeah oh we've got we've got our first entry so robert right now is probably gonna win because he's the only person in the pot so as long as he stays until the end it's robert's game right now i'm gonna have to do it do it as well you know do it i was gonna say i think i think we'll just send you swag as a thank you for showing up on so many of our live streams you don't have to enter if you we'll just send it to you you know i'm gonna let robert take this one all right let's uh let's dive into things um oh we've got we've got lots of winners this is great oh you have to do the win on twitch is the one thing is it you have to do it on the twitch bot uh so you can't do it on youtube you've got to do it on twitch so head over to the twitch platform to do it there i wish youtube supported that stuff that'd be awesome yeah yeah it'd be really cool um okay so let's jump into what we're doing today lee i you and i talked a little briefly last week about what we were thinking and um before the stream started i went ahead and i just did a quick for not a quick fork i ran um create next um which i don't know maybe you could talk about that a little bit i feel like it's a pretty common trend in like the react style javascript frameworks to have this kind of create create function yep so this really all started with create react app so thanks to the react team they wanted to make a really easy way to go from zero to boilerplate for a project essentially so they made create react app which was a command line tool to allow you to scaffold a project quickly you know we followed suite here with doing create next app which allows us to essentially get a hello world application or if you want you can also do dash e for an example and i think there's like 200 examples in the nexus github repo that you can pull in and download and start with so it makes it pretty easy to get started which is pretty nice yeah i am i know we're using it for um our app framework we have the create contentful app as well and uh i'm i'm excited on just how fast it how fast it makes things uh we've got we've got a comment that someone is currently building an nft platform with next js and contentful so uh if we have any any problems uh they're the one who's gonna have to help us so uh because they've already done the work i saw one the other day too uh foundation.app i think um it's an nft platform with next too they're just they're popping up all over that's awesome that's really cool um i don't understand nft in the slightest and i like barely understand crypto i mean i technically i understand crypto but like emotionally i don't understand crypto and so nft might be might be a little too much for me um yes so yeah i went ahead like i said i ran this uh create app and then i also went ahead and i put together a small content model uh as well and maybe we can take a little look at it um so there's two items on the content model there's uh b and b uh and then there's host and so host is a fairly simple one it's uh it's just an author name and a picture and so we have two of them right now we have me and you um so i just stole a photo from from your headshot bio um and then and then we have uh bnbs which are a bit more complicated and so i went on and i googled weirdest airbnb listings uh and i basically ripped them off completely and i stole them so we have the ufo with a slug we've got an address we've got a price and then we have some cool pictures um so you can rent a ufo in the uk we've got a check box of amenities and then we've got a host and then a description and since i'm ripping them off i did make sure to include a include a link at the bottom as well um so we've got a bit of content it's not a ton but i feel like this is enough for us to to get started um as well putting this together and i lea i didn't show you any of this beforehand and so um i don't know if there's there's anything you wanted to change or i know we were talking about let's let's see if we can how close we can get to airbnb in an hour and a half and i've wasted 10 minutes of this talk and we haven't written any code yet so how are you feeling about this no i think we're great getting the uh the content models and the seed data set up is a great start so thank you for doing that no worries i was really excited like my favorite one i just need to show you this is the elephant you can apparently rent an elephant in new jersey so it's this giant elephant and it has a house inside of it and it's the most ridiculous thing i've seen um [Laughter] um you're hosting this one so uh everyone's gotta uh at you on twitter to to get that booking sorted that's awesome um they're so weird they're so weird um cool so we've got we've got an xjs starter and i have it running here um and we've got our contentful stuff where do you you're the expert right like what do you think we should do to get started how we're gonna make this happen yeah um maybe the most logical starting point would be showing a list of all of the different um listings basically that you can see on the home page or on the index route um and then the next step after that would then be clicking into a listing see more information about it so really like the list view details view are the two things we should try to tackle i think let's do it so i guess we're going to need to start by hitting the api to get that to get that list and and i i believe there's a contentful extension but we've also got like the javascript sdk we've got the craftql sdk i'm curious what you what your recommendation of like the best way to like get that data would be um yeah i'm a fan of the graphql api yeah that temple has um one of the nice things about nexjs when you're using git static props which is the function it's the special sauce that allows you to essentially fetch some information somewhere and forward it on your component that's running on the server so you can do basically whatever you want you can use a rest api graphql api node clients all sorts of fun stuff and the cool thing is like i said because it's on the server you can use your access keys and your tokens in a secure way yeah put them as environment variables and not expose those on the client yeah the nice thing with uh with contentful for streaming purposes is for the folks that just saw me spin up an api key if you haven't used contentful before um i spun up a content delivery key and this key is read only so like if you have this key you can get a dump of all of my content which for my stream is like totally fine i don't really care um so um so yeah it makes it makes doing stuff on our side a little a little simpler um but what i what i've done is i just went ahead and i i headed up to graphical so our instance of uh contempo graph or regular graphical that we host on the contentful domain um and i just typed in our space and our our access token so it can if we can we can have a nice space to start writing our queries uh if we need them so like bnb collection you know items let me just get a list and i can i can start pulling the titles um as well because we'll and then maybe the slugs because we'll be using those to generate pages down the line so yeah there we go we've got our first query nice and easy um nice cool um yeah i'm i'm a huge fan of graphical and and just graphql in general i'm i'm like really with you i find it it just makes things so much faster to do so so i'd say next thing would be i mean we could start with this just to get some some on the screen we can get some data out there um i mean we can either do vanilla fetch yeah make the request or i like the graphql request library which just simplifies doing that a little bit is that a next specific thing or is it just a generic graphql request um it is a generic library it's basically just a really thin wrapper on top of some fetch that allows you to more easily make graphql calls so i am down to learn new things i am always up for new stuff so let's do let's do that um this is going to be npm at graphql request and lee give me a shout if the font is too small as well or actually probably the people watching on stream should give us a shout if the fun is too small and wow we've got we've got 41 people watching today that is quite a lot so um while this installs just a quick reminder we do have a giveaway in the twitch so if you type enter win on twitch it'll uh it'll uh hook you up to win some swag and so uh something for the 40 of you that are tuning in if you want to win cool swag make sure to take advantage of that so um cool so we've got we've got graphql and then i guess i can just start copy pasting this right yeah so what we can do is i mean you can copy paste the import for sure and then the query um we're actually going to make inside of git static props so if you go back um to the the github the github again npm i guess it wasn't github in the graphql request yeah um then you can copy the rest of that yeah really um and we're going to dump that inside of git static props so down at the bottom of this file or at the top i guess but you're going to export a function that's called get static props uh so right let's do it right at the top so folks can see it export static props one word keyword export function git static props one word export function get static and then lowercase g as well i am a python programmer and so i really want to be underscoring everything and so you might you might get sick of me in my uh my python i always find it difficult to transition i was writing uh python this morning as well um and so it's uh it's always something okay so we've got we've got an export function right yep and so basically this is going to return an object that has some props inside of it and we get to define what those props are yeah so then you can paste in the the graphical request inside here yep okay and we are gonna have to adjust this query because this isn't the real one that we're using so i can just grab my query from here and copy paste it um there we go okay and let me go ahead and just save it and see if my lint oh there we go my linter is running which is excellent i uh i'm always worried about like have do i have my linter on or not because i'm always switching between black which is the really um opinionated python one and um and eslint for javascript so it always breaks things um cool um another thing we could do here that i'm noticing is so the request that you copied in this is using um just the dot then chaining for the promise so it's making some call but it's not a blocking call yeah so instead what we want to say is we're going to use async await yeah so we'll say um listings and we're going to await this request rather than doing the dot then and we would get back some data from that um and really the only other thing we have to change then is at the top we have to mark this function as a asynchronous function let's do it all right and let me go ahead and update let me actually just clarify that this is the listing query so we're going to have probably uh a query for a listing i can't spell because we're probably going to have a separate query for like hosts and other stuff so yep we've got that and then i guess we need to update our um our uh authorization tokens so let's see if we have cool oh yeah yep there you go so we've got our authorization and our end point and yeah they're actually doing the async way of doing it in here in their example oh perfect we can just use that one then yeah nice i mean i think the one we've got like is working oh yeah mine yeah let me put this at the top and i love linters they are my favorite so you brought up a point earlier that we we probably should think about the way that we're going to do authorization tokens because we're probably going to want to i mean again this is like a like an hour and a half live stream so maybe we can skip it or or but probably not because it's probably imagine like a good best practice thing yeah yeah we can do it really quick yeah so um i think the two uh environment variables we'll use here to swap in values would be the space id and the um authorization token so if you go uh in your directory and your file tree on the left at the root of this project what you'll want to do is create a new file yeah called okay that's a little different from what i'm used to i'm used to just standard.env yeah and and really what this is and then um let me just take that and oh you got it yeah i'm just gonna oh it hasn't showed up on my uh my live shirt yet but i was just gonna drag that file to the root so we're like yeah i think it should be oh it's not in the root that is my bad here we go because they're an easy way to pull it up if you drag it down maybe set it down yeah there you go yep move there we go perfect um yeah and then inside here you can define your environment variables so you'll have something that is the uh typically we use like all caps case for this like uh there's there's another word for that but it's like yeah space underscore id ah so here we have the underscores yes that's so this this is using the dot m uh file syntax which is just kind of a javascript standard that a lot of people use i feel like this is just a standard in general because like i end up doing things this way in python as well um shout out to iris beat loose carter and sociable steve in the chat as well um don't make sure to tune in for our for our win uh our contest uh i know y'all are big white panther fans and so hopefully hopefully this stream uh how the swag thing looks is familiar for you all so um yeah so we've got we've got our tokens set up and one thing here um no space in between the equal sign yeah get rid of them for some reason that new file hasn't showed up on my editor but totally fine oh no you nailed it you nailed it because i've got you helping me i'm used to that i don't know what i'm doing in javascript world you're doing excellent so you have your new environment variable one thing to quickly clarify here to load those environment variables we should restart our server so that it can load those so if you just um exit out and restart it i actually don't even think our server is running i think i have it turned off since we installed the graphql so yeah so yarndev will start up your local server or npm run dev uh and you see it should say loaded m right there from m dot location i love that we don't even have to install anything i'm so used to having to remember to pip install dot env and uh shout out to next yep yeah it's definitely nice um so now those environment variables are automatically available for you key thing just to clarify for for people listening those variables are only available on the server if you want to automatically expose them to the client you prefix them with next underscore public okay that's just a quick thing there it's uh for added security yeah actually maybe you you kind of dive into something interesting there what's the the differ i don't know how to ask this question in a way that i feel like i'm giving you a softball here but like maybe this is like could you talk about like how next handles the breakdown between like server-side stuff and client-side stuff yeah so next is a hybrid framework meaning that you can do uh server-side things client-side things static generation which runs on the server all these different things essentially to allow you to choose whatever works best for your page on a per page basis you kind of get to choose um which strategy you want to use so it's it's not um dogmatic or opinionated in the way that it forces you it's really hey use whatever tool is is best for your page that's really cool i think that's i think i love frameworks that give you a lot of options i always feel like i i'm either i'm really into like open-ended frameworks or minimal frameworks because i find that i'm often just like ripping things off pretty aggressively i really struggle with things like rails just because rails are so opinionated and uh so it's great to hear that um um cool so uh i see i see what you're doing there i'm gonna i gotta make this space id or start using the the tokens yes so basically what i'm doing here is i am using um string templates you know inject variables in and then inside the variable i'm using is process.emb.something so um there we go yeah so then when you run that at you know when it's built it will actually swap out that environment variable for the correct value yeah so i think the missing thing here is using that client graphql client to make the request okay if you go back to the npm docs which looks like they did okay so graphql client dot requests and then yes and we can do the console log and probably also need to return um at some point yeah so you can ditch that one so and then we've got data and we've got listings query um and then where are they setting the end point endpoint right there cool okay so we've got a lot of red so let's see what the expected of function expression did i mess something up um what did i mess up here i think it's not liking the uh gql tag template literal potentially um oh like up here oh wait no i think it's on the hmm i tried to do the eslint autofix to see waiting about i think it might want an arrow function i'm not entirely sure okay so an error function is going to be oh i might need your help on this one um yeah it's gonna be i i think i think what it's looking for is something in the effect of like oh yeah which which i don't think it will work for doing this it's expecting the the default or the name export there but this would be your arrow function um yeah i don't know i don't know if that will work and async goes here essentially um we can try yeah yeah it seems happier it's happier yes indeed okay we are not defining graphql client that's probably because there's a different import here which there is since we're using a different thingy now what is expected multiple before single oh this is a i think there's an ordering problem um yeah there you go cool and then i guess we should we should still do our so where is where is this getting called the aesthetic props function so i'll kind of explain how this is working now so at the bottom you see that if you scroll down there was listings this variable so we fetch our listings and then we forward it as a prop so if i go down to line 34 on this home component and this is where i take the props into the component it's basically like an argument so what we're going to say is take those props and i want listings that's the prop i want and then we'll do our our logging inside here cool so we should see this in the browser so what we're doing here is get static props it's running on the server with node.js fetching this information securely from contentful and then forwarding it at build time to our home component on the client so so to clarify then are we gonna when does it update so like hypothetically if we you know did a publish event would we need to restart the server or yeah so there's really a few different ways to handle this we could either do um instead of get static props we could get server-side props yeah and what that does is it will be up-to-date on every request okay on every request it's going to talk to contentful and fetch that information that works for some use cases for other use cases you might want to reduce the number of api calls you're making the contempo and in that case you can do the static regeneration which was talked about on a previous stream um that's probably the thing to do is the static regeneration for this one yeah and yeah let's see yeah if you if you look at the the return where you have the props yeah um you're basically able to say um i want the revalidation time to yeah i'm like such a huge fan of that that's that's so cool like uh i think that's awesome [Laughter] i'm very easy to please i guess let's uh let's see if this is uh rendering yeah um let's do it um so we've got uh where's my page uh create react app and let me open the console log while we're here yes and oh yeah there it is right here we've got our bnb collection we've got things work on the first try that's the best right steven judith joined the stream five minutes ago and is asking is compiling and the answer is definitively yes is compiling confirmed so we did it we did it that's it i think we're done here yeah we can go home that's that's an xjs right there everyone needs to open their console to see the data and um so we should probably put this on the page then maybe maybe i like i like these cards maybe we can just take advantage of them straight away yeah we might be able to let's uh let's do that let's do it okay um so we can get rid of welcome to we can change our start changing stuff so if anyone has a great job case closed it's good we can go home um welcome to next bnb uh welcome to oh you got it okay this is uh for for those watching this is the first time i've used live share live share and it is uh it's pretty cool we're gonna do lowercase n yeah it's really good i'm a big fan of it um we can probably also remove the console log for listings and we're actually going to do something with that listings variable now so let's go in here and uh we have a grid this this grid and then we have different cards and each one of these cards is a link so what we could do is just take one of these for now i'll cut that and then i'm going to get rid of the rest and then i'm going to jump into here and i'm going to make an expression in this section i'm going to say okay for all the listings let's map over them iterate over the array we're going to have some individual listing and then we want to return something yeah not something that we return um could just be what's it complaining about listing is not defined listings i gotta i gotta type things correctly this is that uh plural plural to singular that's that's getting here yes um so next it's saying we need to use listing we absolutely do yeah class name that's fine since we're we have an uh an iteration here we need to tell react um the key for this array so it understands how to dedupe elements yeah essentially like re-render so the key for this what's the unique property on a listing listing dot i b do we have some kind of we can get one really quickly actually so we've got um we're not pulling it on uh graphical but we we can pull it uh i think it's going to be cis and then id yeah not space id regular id um slug is also going to be unique too i have slug so in contempo i set a validation in the content model to make slug unique so there's there's also a guarantee that slug is going to be unique if you want to use that too nice yeah um it's good to know that you can pull the idea if you need to but given the plug's unique then we can absolutely just use the slug here yeah i figured if we use it for routing it would be i mean that's what slugs are for so yes um we can remove this link for now because we're actually going to change this to a client-side link yeah um but so actually we'll just make this um not go anywhere for now yeah and then inside of here the title is going to be listings.title title i think it will scroll down so folks can see it listings you got a plural uh so inside of that oh right you've you've said it as a singular i see it yeah yeah i see it parentheses around multiline jsx oh i think we might have a uh a prettier versus yes thing yeah you see how it removed yeah yeah i can turn my es lint off if it's if it's conflicting with your uh your stuff yeah you might as well yeah you want me to turn it off um okay it's tough because it might find something that we need though there's pros and cons to vote yeah let's leave it on and if it doesn't compile we can we can do something um as well um hi to everyone in the chat jason i see your question and we will get to you in just one second so um should we check it out see if it compiled see if it ran yeah let's do it oh listings.map is not a function nice so i think what it's saying is do we need a parenthesis object is not accurate so what was probably wrong is the data object we're getting back from contentful is an object and then there's a key that we need to use so it's probably uh if you scroll up to like line 29 it's probably like this is probably data and then this is actually listings and it's like data dot listings yeah because of graphql that would be my that would be my assumption um we could console.log out again to check but let's try that all right i'm saving it and then yeah so let's let's console like that then just to double check for sure um can we do that here is the fact that it's not i guess we just do it on the on the server yeah we can do the server as well um really there's two different ways i mean we could destructure that here on the server or we already had it on the client we could also just structure it there as well too yeah let me just do it here and hold this up filling compiling and then you'll have to refresh your page oh yeah and then if you go back over you should see oh this is kind of messing with me you see it bnb collection is what we called it that's what we called it okay so that's what we want it to be so and then it's uh data.bmb collection pb collection there you go and then we're okay with the dot items or do we need to go another level deeper to get into the array oh good point good point yeah so it looks like bnb collection then dot items yup that should do it that much save it out successfully we got a new error this is great this is progress refresh i wonder if it needs to fetch that new day there we go we've got our three things nice i like it um yeah so i guess we've got we've got a couple options here because we've got we've got our pages generated and um oh while i've got you let me let me pull up jason's question um i didn't want to interrupt us in the middle of stuff but this seems like a good pause point lee how would you configure your contentful calls if you're wrapping content in user authentication and then he references next auth and magic as potential libraries would you use get server props to check for user offs and move all contentful clauses to the client yeah so i think the underlying question here is like where do i want to do my authentication logic because if you have it on the server using git server side props you're essentially saying if this user isn't authenticated then redirect away and only make the fetch to fetch information about that user given that they've succeeded the the authorization check basically the authentication and the authorization check likely um so if you you can either do that on the server or you could defer if you're doing off on the client side to wait and check once you're in the react space um whether you want to make that api call yeah i the one thing i would drop in there is that i don't think i would necessarily recommend making those contentful calls on the client because if you do like a network inspect you'll be able to see the call and um i don't know maybe you can get the tokens out of that and then you'd have access to everything regardless of authentication so because we don't have anything like that on the contentful side you would have to move the logic the the logic to fetch the information from contentful would still live on a server whether that's your server and api route in xjs or something and then when you fetch it back on the client side you would pass in some header to say hey am i authenticated to be able to to use this information yeah definitely and i i see khaled in there hey hey there watching on my tv and sending chat messages on the twitch the twitch mobile and then huge qt throw it in throw it in an answer as well to jason's question you could perform the authentication on the server i've used aws cognito plus next ssr it works well after auth is done you do the contentful calls on the server and you're taking the api keys off the client so shout out there um i love i feel like every time i look at aws or like have an idea it turns out there's an aws service for it it's so difficult like i haven't heard of aws cognito but uh i'm not surprised that aws has a has a service for authentication uh i just you know they have a million different things and it's always difficult to find out what's going on with them so there's a lot of stuff that's for sure yeah okay so i want to figure out how uh and and definitely folks in the audience oh go ahead oh i was gonna say maybe we pull a description of what these listings are not on the model anywhere yeah it's we've got descriptions and we've got multiple photos as well we've got addresses but the thing i'm really curious about is how do we do page generation because we've got a dynamic route now and that's that's the thing i'm most excited about for sure we can jump right into that yeah i think that's a little more complicated than than making sure you're putting data on the page it's like how do we come up with these dynamic routes yes sir yes absolutely um so first things first we need a page so we need to create a new page in the pages directory and it's going to use this special sauce called dynamic routing yeah so using um brackets you get to say open bracket and then a name of some variable so in this case it's the slug the slug for a listing yeah and then after the bracket.js what this is saying is hey this is a dynamic routed page basically i can provide any slug inside of here and it will forward that information to my page yeah another thing too here we probably want this um this url to be uh slash listening that was about to be my question was because we could also do like a hosts thing because we've got the lee hosts and the shy hosts yes so yeah i want listing slash something like that yes i think that will automatically make the folder in vs code it does yeah nice so okay yeah so now we're in here um i mean we can copy paste index to get a shell in here the whole thing sure there we go and uh if you scroll up to the top there's two pieces here yeah we talked about git static props that's how you're fetching the information that you're displaying on the page that's how you're saying what are the listings and in this case what the specific listing is that we're looking at the next piece here that you're alluding to is i have 10 000 listings how do i make all these pages for all these different listings so the uh the robin to our batman here is get static paths yeah so basically you use git static paths to fetch all of the different listings and tell next.js hey what are all the urls that we need to generate so we're going to need to replace get static props with that or is that going to be two separate functions yeah it's actually complementary so you can both yeah uh so so new function then export con uh i can save you uh a title before go for it um you copy this from the docs so much easier yes um this this will give us some talking points here because oh did i copy get static props you did copy static props my bad let me grab get static pass the reason i like to copy this one is because the structure has to be exactly right um so what we're looking at here is again similarly named function get static props and the return involves two things which is an array of the different paths that we want to forward to our component and also fallback and basically fallback what we're going to do for right now is just say this is false yeah what this means is if i try to go to a path that doesn't exist it should 404. there's more advanced things that we can do if we want to get into the true scenario but for right now let's just do a fallback of 404 yeah so really what this paths array looks like um just to show like a tangible example here um in this case the dynamic route is id yeah instead of slug and then we're telling it hey here are the different ids so this would be like uh slash listing slash one or you know slash the only thing that's different here is instead of id it's slug plug and since it's not hard-coded two we basically want to generate this array and each one is in the format of this so we can do is just take that first one i'll cut it um we're obviously going to drop in the listings here in a second but let's just assume that we had listings right let's assume we had listings again we're going to have i did the same typo listing uh i have one listing okay at least you can spell the word listings correct on the first try that that i've been struggling with i keep typing in listening which is which is not right at all so we returned that structure for the object and then for slug it's you'll be listing start slug yeah i saw the e there i i ruined your mind so that's some of it we're we're closer there but now we have to actually get the data yeah um so one thing that we could do um we i guess we probably don't need to do it here but for maybe best practices is now we'd be using this same setup for graphql we're doing the same connection to contentful we're doing the same setup with the environment variables so what we probably can do is just abstract this out into some library or some other utility or however you want to name that uh and then basically all we have to do is import our client and we can make some requests to it let's do it yeah and we would just set the function on the page level and pass the function as the thing because i imagine we're going to we don't need like the title or anything like that we can just get the single slug here get that lightweight call um awesome or we could actually probably just reuse it just create a single listing query and just pass that in absolutely multiple places um cool so where where would be the best place to put that at that function so everyone has opinions on what these are called some people call them like a utility folder like a library folder it doesn't matter i mean really whatever we want to call that but at the top level basically we would have a new folder that's like you know utils or library or something to the effects of that and then inside of there we can have a new like contentful pills and then new file um do we want to separate these out or just have all our contentful stuff in here well let's put all the contentful stuff in here contentful helper i'll call it um cool and then we're gonna probably grab all of that graphql stuff yes basically everything that we use to make the initial graphql call we can now pull out into javascript file because it it's javascript yeah um helper and then endpoint listing query and there we go and then do we will need to put this in a function yup okay so that's going to be we're going to export this right export yep we have it set up as async so we probably won't i'll just steal this and rename it for in a sec yes we call this um toss the thingy at the end get listings and there we go it autocomplete her audit formats for us which is amazing finally here we just need to return this and when you have async away you actually don't need to write a weight on the end yeah it will automatically await that when you have this function yeah and we're getting a error what's the error anybody's needed a space oh perfect cool um and then in here i'm guessing we can do we need to import contentful helper yeah so imports and we only need the get listings function um dot dot slash dot slash great there we go auto autocomplete is too helpful um and do we still need this to be async at this point or do we can we drop that yeah yes so now inside of this function we'll say const listings equals await uh get listings and then call that function and now we can copy paste that and use it and get static paths too so we abstracted that logic out to a shared utility and we can use it in both places amazing i love reducing stuff yes we actually call it data i guess but we can did we call it data yeah we can just call that data there we go right and then we'll need the import and then we are good data and let me steal this and what's neat here too is um well we'll get to this but then in get static props on the slug page we're actually going to fetch a individual listing instead of all the listings or it should be sorted alphabetically thanks thanks yes lynn i don't care enough to fix that i feel like that's a fixable a fixable problem it's like that's one of those things that should just be allowed to resolve for you yeah um cool and i guess um so we can double check that this is still working amazing so cool so we are back in our slugs route and we can use that function again here right yes we've got data um and then here it's going to be data dot items dot slog wrong one yeah um do that on here let's do that oh so just do data items there we go yep cool that should be good um we've got a lot of red it's uh the same thing where it wants this arrow function all right right paths i love how easy this is this has been really simple so far like this is pretty i don't know this to me just all like clicks in my head so nice that's good it's nice when the the api for generation is simplified to what like 10 lines of code makes life a little bit easier yeah so one question we've got from actually uh since it's another this is heading back to the questions about auth huge qt is asking does incremental static regeneration work when using server-side auth how are the results cached when the all-state changes yeah so if you're if you're doing incremental static um generation you probably are isn't something that's going to be using off probably it's probably something where you do the off in the generation not necessarily at request time so the mental model to help you understand this is is it something that needs to happen on every request or is it something that can happen in the background and um be stale per se um so in in this case if you really need to have authentication on every request you should probably use server rendering in this case yeah that makes sense that makes total sense it gets really uh it's very specific to the use case so it's hard to give general advice here but um we can dive more into that later for sure let's do it yeah um let's keep going then so so we've got our routes and we've got our pages um do we want to return like like just a quick html thing so we can see what's in there or would we get like right now it's just going to be an empty an empty page right if we go to listing ufo or something or oh no we've got we've got right here we've got stuff yeah so what we want to do here um you know they're currently we have this copy paste we can just remove this for now what we really want is inside of here to say like um instead of all of this stuff yeah let me scroll down to where you are um we want to just say like this is a listing yeah so now when we view this page we know we're looking at a specific listing if we go back to our browser let's make sure that the actual urls are being generated so we should be able to go to slash listing slash sum slug now do we want to install the link because i believe next.js has some link handling too yes yep let's do that now um so to verify that works what we can do is import link from next slash link or inside of our index yeah next oh and it'll be a separate report i think links or i guess it'll it'll autocomplete for me amazing yes and then down in our listings where we previously had this a tag instead of the a tag we can have a link tag let's do it and uh instead of an href well we still have an href but now it's going to be um we're going to build it dynamically i love it um so we want to look at the slug to determine how to build this yeah so really we can do the tag template literal again here where we say it's slash listings um yeah that looks that looks accurate looks like the need that i'm still still complaining about the parentheses but that's okay i see i see it compiled successfully i love that you can actually save this on my like the link the visual studio link sharing is actually really cool but you can save it you can force the save event on my on my file so um okay so we've got an error here react children expected to receive single react child yes so what the link tag is it's expecting us to have a single uh child here so what we want to do is wrap this in something yeah oh there we go give me the auto format maybe or not maybe i have to hit save nope it's just not picking it up oh yeah the first child needs to be an a forgot about that okay there we go got a successful compile oh and we lost the card element though it's a bummer but that's okay i see the links are working i don't know if you all can see this it's kind of small but uh um maybe i'll just do a quick inspect element to prove it and yeah you can see that we've got things working just great and if we click into something we get uh is that not working is that click event not there it is when we get a server error but we did we did get a url update so that's progress we are close it looks like uh which makes sense because we're requesting data in a weird way right now yeah uh additional keys return from git static props oh i think i think i might have typed something in there wrong if i go oh yes okay so if you look at pass yeah um really what we want here is this array it needs to be nested under params i i uh fat fingered that one so instead of this there we go cool that should work let's try refreshing that yeah let's do it and the reason if you're wondering like oh why didn't that immediately show up when i clicked on it it's because when we're running the dev server pages are compiled on demand if you run a production build then it's you know it's instant but it does that to help scale when you have you know 10 000 pages you don't want to build them all ahead of time yeah that'll that'll ruin your machine or you'll be waiting for a while at least um so okay new error invalid pass value returned uh must be an array of strings or an object in the shape of params key string string which oh maybe i was wrong maybe i was wrong maybe we need do you need the no we have it params maybe i was wrong because yeah it should be i think i read that error wrong yeah oh i bet i know what it is i wonder if listing.slug uh isn't a string huh it should be i mean if returning it on taking a look at the graphql object it should be a short string but unless there's something happening in the middle there that's just a thought yeah let me try try refreshing now i want to see what that other error was yeah absolutely yeah [Music] so pass do we did the server oh we got an error in the in the console so here we go yeah it's probably the same it's the same error yeah key of a string and it returns a string i don't think it's saved there give me one sec actually let me refresh now that i saved it yeah there it is so it is something different now it needs to be nested we forgot uh mmh from uh periscope is saying that we forgot the array braces since map returns an array we forgot the array which makes sense so uh i think it would be right here right is it one one lower no i think we have that right hmm yeah i think we have that right i'm just looking at the docs right now and like this was an example where uh you know even even i still look at the box all the time so i don't i don't feel that they're saying that it should be removed i meant it should be removed oh yeah we didn't spread it yeah because this is going to return uh yeah yes we were doing uh nestle oh we were everyone's favorite early is my favorite there we go next b and b this is a listing it works powered by vercell nice i think yep that's perfect you know i'm really so so one one downside to to using visual studio code right now is lee you know this but i am a huge hyper fan boy and i am really bummed that i'm i'm building a thing with someone who works at versailles and i'm not showing off my cool hyper hyper config so you're gonna you're gonna have to take my word for it that it's that it's cool so but for sure it'd be cool if we could embed that right in the video yeah oh that'd be great i mean i do have the zsh stuff set up which is which is fine but it's even cooler when when hyper gets involved and so if folks are looking for a cool new terminal to check out that's that's my like heartfelt endorsement for for my favorite tool that i use daily is hyper for sure um cool so we should probably build a specific query then for the individual listing right yes cool so i guess let me head back over to graphql and um so we're going to be filtering here by slug so where and then i think it's an object so slug slug and then is it equal so let me pull up the query docs because i don't know this off the top of my head um slog string so yeah slug and then this is going to be a variable so for now i'm just going to set it as treehouse so i can double check that it works pool so we've got our title our slug and then we're going to need all of our um stuff so what is all the stuff um let me go back uh and we're gonna need a bnb bnb so title slog address because people are going to want to know where it is price photo collection items url yeah and then amenities which is going to be a list so i made it a list and then host name photo buy it i mean address oh i haven't used the address in graphql so i actually don't know what's going to happen it gives us a lot long that's kind of annoying lat lawn and the photo here is going to be a url as well sorry for this lee i hope i'm not boring you with with typing all of the things out um i think that's everything and then description which is going to be rich text so we'll need to install a rich text but there we go does this look all right um so yeah we've got our address we've got our price we've got a bunch of photos we have description which is going to be using contentful's rich text then we have amenities which is a list because it's just a checkbox i i just set it up as a checkbox we can check things and then we've got links to the hosts i think this is good i think we can get a lot of this maybe we won't use all of this but it's good to have i guess all the data there um so i guess jump over we'll probably do this in the helper function yeah yeah let's jump back we can make a new one uh get list sting which is going to take in some slug that we forward to the query while we're here do we want to maybe pull um this stuff higher yes we can instantiate all that outside of the functions because we're going to reuse it right and then uh it doesn't like the arrow function again but that's okay oh it's that's strange it's all right i think you oh it's fine i was gonna say you could throw in a weight on the before the return so return um like here yeah i think that's an esl thing it's complaining about let's let's ignore it then i don't think it's going to be the worst thing in the world i'm i'm actually going to change this to be in no listing singular is fine i guess you and i will just have to make sure we don't get confused anymore than we already have yes cool there's our and then uh this is going to need an argument right so something like this right but no not right there something like after the async for the async so yes yep so now you forwarded slug as a parameter to this function and now you can use it somewhere so let's double check how variables are being handled for here um so it's being used like this and then okay i got it it's gonna be query is gonna declare it and then query get movie so it'll be on gql oh i get it okay so it'll be query get listing and we're going to be using a slug so we can then do yeah dollar slug which if i can find the dollar on my keyboard dollar slug and then variables yeah this is gonna be slug slug oh it doesn't like that yeah you can just do a es6 shorthand here so yeah you remove the second slug um it will just automatically make the object and the electricity coolant that's really cool i really like that this is the column as well too down below like that yep so it'll automatically map that to slug colon slug it's just a shorthand notation yeah um and then we're going to be doing we're changing how the request works and it's going to be yeah if we want we could even just save ourselves a variable here and just put it directly like that let's do it uh and then i guess i can undo renaming this query i renamed it because i figured it was getting a little crazy but i'll undo that so this should just work then nope give it that extra space i mean it compiles successfully so hopefully that means it works you will find out very shortly okay all right let's head back to the get get listing and okay so so now inside of here instead of getting listings and get static props so we want to fetch only the information for this page so we want to get a specific listing yeah so let's change it from git listings to git listing is that going to mess with the paths out of curiosity or oh no we do that here we do that up in the get static path so you are correct so get listing singular and then we're going to need the route right so yes so inside of git static props the first argument of that function yeah just pull up that file is something called context yeah and this context object gives us access to information like oh what was the actual route that was passed in essentially so then inside of here given that we know the context we can say get a listing and we can say context dot um i think it's query dot query dot uh slug is the dynamic value let me double check what the uh the values yeah while you double check i'm just gonna go ahead and make sure that we're also importing this function because we are not currently importing this function it was params not slug so contact.params dot slug should give us that dynamic value oh yeah we have our we have our super user from periscope who's jumping in and said it was params as well nice nice um yeah so that gives us uh data about an individual listing yeah um so then get static props rather than list stings we can do a single listing and then uh we need to double check what the structure of this milk query will be returned as yeah uh mhmdo is saying i'm loving it sorry crying emoji i think we're loving you for helping us so it's all good keep it keep it coming it's easy to miss things live so we appreciate you there's so much additional pressure when when you have uh 40 something people watching you writing yes um cool so we've got we're returning our prop with our singular listing with our item this might actually be i think this might might need a oh this is not what i was looking at we might need to since we now have it set as dot get listing we might need to add a dot get listing in there but well i guess we'll find out and see the names the name of the uh the name of the model it looks like it's still the same bmw uh i don't it'll be single it'll be there'll be one item in that array yeah and we can actually that's a great point maybe we can do this as a let me take a look at how the bnb looks oh yeah we can actually no so this needs an id is the problem and we're doing a lookup on a slug looks like white panther just joined us um as well so yeah this is using so if we filtered it by the id we could do that but since we're doing a filter based on a slug we would need to do it in the collection which is the limit so it works um i think it may be our sdks you can do you can do filters on fields but because you know in theory like the no you wouldn't be able to do it on a singular because it doesn't know that contentful doesn't know that we've set it as a validation to be singular and so like um for all intents and purposes it could be an array if we didn't have the requirement that we're only allowed a single slug and so i think that's that makes sense that it's set up that way so that it returns an array and we have it configured on contentful so we as developers will know that it's always just an array of a single item yeah so i think we should be good then data. bnb collection and then items and we're going to get first item that array which should be the single listing yep um just uh like as a sanity check what we could do here do a quick console log make sure that data's right but then when we save we're going to get our listing back and we forward that to our um we'll just call this like a listing component here we get that and then we can use it inside our component line 37 to like put up start making it look nice and yes let's try that cool let's do it um oh errors oh no all right what have we got uh expected dollar sign yeah this looks like an error with our graphql query yeah that looks right as well maybe the way we're doing the variables is wrong so let me pull up the helper um did i do the variables right let me take a look at that documentation okay i think it's single i have it in oh yep you cleaned it up for me so i think i think that might be it yep and it just outputted a whole bunch of stuff in the console i think i think we're safe uh let me refresh this there we go we've got our we've got our title nice i love it um so i guess we should start styling this page damn it was i was slow from uh from mhm1 um who was our number one problem solver for the stream was too slow this time um yeah so i guess we we have our data here and and it's probably time to start making things look look nice we've got we've got about 20 minutes left on the stream and i don't know if there's any other um i feel like we've done a lot today so far we've gotten we've got next started we've got it routing which i think is is always a good thing to learn we've got collecting the data we've got utility functions are there any like big things that you think we should we should cover before like we we heading to the end of the stream or yeah um i think what so i have uh when i do streams uh folks are usually divided like some people love to see the styling and they love other people think it's like super boring i'm a backhand guy so like for me it's all about like i want to see how things are working on the back end so one thing we could talk about like assuming you know we throw some polish on here we make it look a little bit better but the next thing would be actually getting some images on the screen maybe yeah maybe we put our uh like an image of the of the absolutely the treehouse does next i know next has a bunch of utility functions for images which is i'm hoping where you're going with this yes so let's go start let's start with graphical and see what what that format looks like on the data back from the images yeah so we've got url right two sets of images here we've got an array of four images roughly and they are yeah they're https urls and then for the host we have a single array with uh with the headshot as well got it so let's start with the with the photos of the listing is there any more information other than url on yeah so we can get we can get the title which i like to use as alt text as well is there like a like the size of the images too we can get that yeah we can do width we can do height um we can actually just take a look let me pull it up on the uh thing those are really the ones i was most curious about yeah so we've got yeah we've got height and width and then on contentful we can also do transforms we can do image transforms on the api request if we need to okay yeah let's uh let's let's stick with that for now let's add those back to our query in our code absolutely i can do that so let me go back to the helper and it's the big helper photo collection um and it's too many items so let me get rid of that it doesn't it doesn't format my graphql which i guess makes sense because it's a string yeah i wish it did though that would be lovely um and do you want me to go ahead and put it on the host as well or um that's good for now yeah i think that's it for now cool all right so it's updated so now if we go back to our individual listing yeah we can basically go down and where we were looking at the description yeah what we can do is use next image and what this will do is allow us to get a automatically responsive and aspect ratio maintained image as well as image optimization out of the box i love it and there's really only two things that we have to do here one is we obviously we need to use the image component so this is some uh copy copy pasta in here you have a source an alt tag a width and a height which matches exactly to what we just talked about so we have a listing dot something was it collect bnb collection or it was uh you want photo collection dot items so we're probably going to want to do uh another map function yes or just use the first one either either or well uh we can start with the first one yeah um and then dot so then there's url title width and height perfect so for now we'll just throw this all inside of here um title yes i know this could be better but it's okay it it demonstrates the purpose so we haven't committed the code yet i feel like it's fine until you commit the code somewhere yes and then we scroll up to at the top we need to import that image function or that image component yes so this is half half of the puzzle um only other thing remaining here is for the built-in image optimization we need to essentially have an allow list of urls so that um basically our application knows hey it is okay to go out to contentful and you know fetch these images from an external domain yeah and optimize them yeah so to do that um basically we create a new file at the root of our directory um let's see i'll see if i can i got it oh no that's not where i want it um did it pop up for you uh what's the there we go next.config.js so we're overriding our next config and we're basically saying hey um what domains do we want to allow yeah if we go back to graphical um it looks like it is images dot yeah whatever that images.ctf dot that's what you want to copy paste and put inside that array there we go nice um you also see there's a line in our server that says hey you changed the configuration restart restart the server we're going to make it so it just automatically does that for you but at least it says something so now it should be able to go fetch your image from contentful and optimize it cool so we should refresh that page and i love that it picks up the refresh and oh no cannot read properties of items undefined is it a typo no those there we go let's try it i gotta learn how to spell one of these days oh you me both right it's the the hardest part of programming there we go we got it we've got our secluded in-town tree house this is in atlanta georgia that looks amazing i yeah i want to to stay there according to their listing they are the number one requested airbnb on on the platform which uh which is really impressive yeah but like who could blame them for having for having a treehouse in the middle of the woods if you're looking to get away like that seems great so something interesting here if you open up dev tools and you look at this image um there's a few things that it's doing that are kind of interesting so if you click on here you see you have an image tag but it's also surrounded by a few divs and what those divs are doing is maintaining the aspect ratio for you so we provided a width and height for this image we could scale that up or scale that down and it will maintain that aspect ratio yeah and then you also on the image tag you see there's that source and underscore next slash image you see it's making a request out to um get your image from contentful and then it's going to do optimization on this it's actually using web assembly to do the image optimization it's built into the next js server so when you do next build next start it just it just automatically does that for you that's awesome yeah and i can see that there are these url parameters on the end but it um yes and that's happening on the next level not the contentful level because these i think are identical to the contentful url parameters too i feel like the url parameters on images are pretty consistent across the internet yes and you can also swap this out if you want contentful to do those transforms or that optimization for you yeah how does this handle caching so then this would be stored in the in the build rather than the i mean i guess it would have to be stored in the build then and and would like assuming we deployed this to versailles or something versaille would handle the caching for us here yeah so the way this works is these images are optimized on each request and then they have caching headers on images such that you know when you come back it's already cached for you so it's uh it's it's as long as you're using next start essentially which is how you start the next gs server um it works wherever you want to deploy if you want to roll your own server if you want to deploy for cell whatever whatever you want to do that's really awesome yeah that's that's pretty similar to the way that contempo handles our caching as well and so i think that's i think that's really great that there's a you're able to like kind of keep everything together and yeah as well which is really cool so i'm trying to think of other big more functionality things wise here other than styling like i would love to install the contentful rich text library if we have enough time for that so that way we can actually get some uh text out so is there a next renderer or do we just want to do html safe uh let me see if there's a next renderer i don't know if you know off the top of your head i don't know off the top of my head you see how stella is doing it in her uh github she has a neck starter that she's been working on for a few weeks and um she has already solved this problem for me i just sent you a um in the private chat yes amazing there's a example of a a blog basically that's using contentful yeah um and then i guess we'll look in the pages and see what they're doing in the index i think everything is inside of maybe api maybe api oh wait no we're we're in pages we need to go back to the the root directory yeah if you go back to cms contentful yeah so it's probably oh yeah contentful looks like this is just setting up information so if you go back um go into the library folder got it api so api is how yeah it's probably using the graphql api to information uh and then oh i bet it doesn't have rich text though no i don't think so so let me see how sama is doing it because i know salma is doing it in this one which text import rich text page content so she's done it in components um rich text index richter so she's using the react renderer is what it looks like that makes sense so let's install that hopefully we can get this done in a couple of minutes uh i unfortunately have a meeting and so i have a hard cut off at 3. no but yeah i guess last call on the prize the prize thing um as well so we'll be doing a dream the drawing for the prizes in just a second uh you just have to do it in the twitch chat you have to do the dash win in twitch um as well so uh and you have to be here to win as well that's the other that's the other thing uh because i'm gonna have you whisper oh no we got an error unable to resolve dependency tree maybe we just have to call it here then we've only got so much time and i don't know if we'll be able to solve that in in the last couple of minutes but no worries or we could just have an error i was just fighting one of those this morning as well i'm just gonna have to have you come back to finish this out is the real solution here there's so much more we can do here we've got a solid base but we can take it way further yeah yeah i i really enjoyed it i feel like i learned a lot today and hopefully folks in the um in the audience have learned a bunch too and uh this is a lot of fun thank you so much lee i really appreciate you joining us for this especially so last minute i know we only talked about this like two weeks ago and you're already here and it's great so um for the folks on the internet who want to learn more either about you on a personal level or you and like learn more about nxjs and get started uh you want to talk about these links really quickly or yeah sure um if if you liked what you saw today there's more on my twitter come come give me a follow shoot me a dm if you have any questions and i will try to help you out um yeah i just tweeted out a cool example today as well for a new blog oh yeah um starter that we did so lots of i like this design thank you yeah i actually uh designed it myself this is this is lovely i love color i'm like uh anytime it's there's more than startup blue i'm always excited for it absolutely i saw i say while we're like surrounded by blue in the in our stream that's awesome and then uh the next js slash learn is the yeah so yeah and then if you uh want to learn a bit more we have a interactive tutorial here that teaches you how to use everything we talked about today and more and it goes a little bit more in depth on some of the concepts behind some of the things we talked about so i highly recommend checking this out if you want to learn um some of the basics yeah i see that you've got oh and there's links into the documentation which i love i love docs yes awesome all right i think it's i think it's time for us to do our prize drawing yeah let me go ahead and put it so it's just the two of us here uh so last call and oh let's see what we got we've got a let me just take a screenshot of this page so you all can see see what i'm seeing and it'll show you the full list um this is salma did like such a cool job on this i'm like so into this like this made my day when she made it um so that's the and the heads are shaking as well so it's very it's very uh it's very cool um all right lee i'm gonna hit the button i'm gonna hit draw so all right it looks like our winner today is huge qt um so congrats to huge qt are you here oh no sorry we've got two winners uh i click i must have clicked the button twice so we've got two winners we've got huge qt and showing wald nils are you two here i hope so um yay cool huge qt if you could whisper or actually probably the easiest way for you to do it is to just shoot me a dm on twitter if you could shoot me a dm on twitter um i guess i don't know if there's a way to prove that it's you is uh whisper to us how who what your twitter is or your email or i don't know what's the best way to get your info out of this i haven't done one of these before so i don't know how to maintain your privacy while also making sure that it is actually you any advice here lee or osama in watching him could explain to us how this works yeah that's tricky especially if they have an anonymous uh anonymous twitch account yeah i don't know if there's a private chat yeah do the do the whisper function um and uh oh you just said what your twitter handle is that that's perfect just dm me from your twitter handle dm me from i will be keeping an eye out for that twitter handle if you could send me your address and phone number because fedex requires phone number and i will make sure that there's there's swag sent to you uh as soon as i see it and again that's it from us today lee thank you so much for joining us i really appreciated this this was a ton of fun um and uh i will see you all back next week let me actually see what the stream is for next week before i before i head out let me plug that really quickly um so next week we are having me and salma and we're going to be doing stuff around the contentful migration cli and we're going to be working on what her website whitepanther.com which should be a lot of fun so join us again here next week um we'll be back to our 11 o'clock eastern time zone and uh that's it from us we'll see y'all later bye everyone bye
Info
Channel: Contentful
Views: 3,219
Rating: 5 out of 5
Keywords: Contentful, NextJS, Next.JS, Learn NextJS, Learn Next.JS, Contentful GraphQL, GraphQL
Id: bMRQgguzBLs
Channel Id: undefined
Length: 86min 2sec (5162 seconds)
Published: Wed Mar 24 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.