Using GraphQL With Gatsby.js
Building A Travel Guide With Gatsby, Neo4j, & GraphQL: Part 2
Looking at the different ways Gatsby uses GraphQL at build time. We update the Gatsby Starter Blog to pull in data from our Neo4j GraphQL API.
Links And Resources#
- Hey folks. 01:35 Welcome to the Neo4j stream. 01:39 Sorry for the late start, 01:41 had some technical issues, 01:43 but I think we're all good now. 01:47 So today we're going to pick up where we left off last time, 01:51 working with Gatsby and Neo4j. 01:57 Here's the project we're working on. 02:00 Lets drop this link in the chat. 02:07 So if you missed 02:11 the session last time, 02:12 the recording is available on YouTube here. 02:15 You just go to that link in the chats, 02:19 you can see the code and see what we did last time. 02:23 Basically what we did 02:25 is we created a new project 02:29 using the Gatsby's blog starter. 02:38 And that uses some markdown files 02:42 to populate a simple blog using Gatsby. 02:48 We took a look at how Gatsby populates 02:54 that content using GraphQL, sort of a brief look at that. 02:57 And then we created this other project 03:02 that I put in Neo4j-GraphQl directory. 03:08 And our repo which is a GraphQL server 03:13 for Neo4j. 03:16 So using, make this a little bigger, there we go. 03:20 Using the Neo4j-GraphQl JS library, 03:23 we defined some simple type definitions 03:28 for a Neo4j database containing Points Of Interest 03:33 from OpenStreetMap. 03:34 So we talked a little bit about how we imported that data 03:38 in Neo4j using the OpenStreetMap importer. 03:42 We used a Neo4j sandbox instance 03:47 to populate this. 03:48 So here's where we're connecting to that sandbox instance. 03:52 And then we configured Gatsby. 03:56 So if we jump to our Gatsby project, 04:00 and take a look at gatsby-config, 04:05 we used the gatsby-source-graphql. 04:14 Where is it? 04:16 Here it is. 04:18 This gatsby-source-graphql plugin, 04:21 we used to kind of stitch in the GraphQL API 04:26 that's connected to our Neo4j instance 04:28 into Gatsby's GraphQL API 04:31 under this POI fields, 04:35 to sort of like a namespace 04:36 for our Neo4j-GraphQL API. 04:42 So what I wanna do this time 04:44 is pick up where we left off 04:47 and actually use the Neo4j-GraphQL API 04:53 that we've stitched into the Gatsby GraphQL API. 04:57 I wanna use that to actually populate 04:59 some content in our blog. 05:01 So I wanna show on the Index page, 05:04 I wanna show a list of all of the Point Of Interest, 05:07 I guess that we have in the database 05:10 and then for each one of those Points Of Interest, 05:13 we'll create a Gatsby page 05:15 that has some detailed information 05:17 about the Point Of Interest. 05:20 So the first thing we need to do is 05:26 let's go to our Neo4j-GraphQL code, 05:29 we need to fire up the GraphQL API 05:34 for Neo4j that we created. 05:39 The GraphQL or the Neo4j sandbox that we created 05:45 has probably since expired, 05:47 those are temporary 05:50 that live only for about a week. 05:55 So I'm gonna go to sandbox, 05:58 oh and this is still a... 06:02 We call them hidden-use case, 06:04 you need to visit this specific URL 06:07 to post in the chat. 06:10 This specific URL to sort of unlock 06:13 the hidden OpenStreetMap use case. 06:20 And again the reason it's hidden 06:21 is because we haven't created the guide page for it yet. 06:25 So the data is public. 06:27 It comes from OpenStreetMap 06:29 that we've loaded into Neo4j. 06:32 But until we've built the guide content for it, 06:38 we leave it as hidden. 06:39 So for now, you need to use that URL to unlock it. 06:43 Okay, so this is spinning up a Neo4j instance, 06:46 that we will then connect to here 06:50 from our GraphQL API. 06:55 Let's use environment variables. 06:57 So instead of sort of hard decoding our credentials 07:03 here in the code, 07:05 let's use a dotenv file to do that. 07:13 So I'm going to create a new file, 07:19 that we'll call .env. 07:22 So here we're gonna define some environment variables. 07:24 So we'll say 07:26 NEO4J_URI, 07:29 NEO4J_USER, 07:31 and NEO4J_PASSWORD. 07:35 And now that our sandbox instance has spun up, 07:40 go to your connection details, 07:42 here's the NEO4J_URI, 07:44 that's the Bolt Protocol IP import, 07:52 NEO4J_USER that's just Neo4j, 07:55 the default database user 07:57 and here's the password. 08:04 Okay. And then 08:07 let's use the dotenv package. 08:15 I think that's this one. 08:19 Yeah, so this is just a handy package 08:20 that allows us to read these dotenv files 08:25 and set those values as environment variables. 08:28 So I'm gonna go into the Neo4j-GraphQL project, 08:35 npm-install dotenv. 08:41 And then just like it says in the README, 08:44 we just import that 08:46 and call it Config function 08:50 dotenv.config 08:54 and then down here, 08:56 instead of hard coding my connection string 09:01 that should be process.env.NEO4J_URI, 09:09 process.env.NEO4J_USER, 09:19 and process.env.NEO4J_PASSWORD. 09:27 Okay, so let's do an npm start. 09:35 I can type. There we go. 09:36 And says GraphQL API running on localhost 2003. 09:46 Looks like it is. 09:47 Can we get some data, Point Of Interest? 09:50 Give me the first 10 Points Of Interest. 09:55 Give me name and type. 10:00 That's unauthorized due to authentication failure. 10:06 Why is that? 10:12 URI user password. 10:17 Where did we do wrong here. 10:18 Process.dotenv.NEO4J_URI. 10:22 NEO4J_USER, 10:27 NEO4J_PASSWORD, 10:35 require('dotenv') 10:39 Yeah. What did we do wrong there? 10:48 Alright. 10:50 Let's log these things to make sure 10:54 actually get them process.env.NEO4J_URI 11:01 console.log.process.env.NEO4J_USER 11:10 process.env.NEO4J_PASSWORD. 11:19 Well, yes. 11:19 So URI user password. 11:23 Did I misspell it? 11:25 Oh, lowercase J. 11:28 Uppercase J. 11:36 And I have to restart that 11:37 because our watcher is not picking up changes 11:41 in the dotenv file. 11:48 There we go. Okay. 11:49 So now we've got some Points Of Interest. 11:52 Cool. I'll take out those logs. 11:57 Okay. So our GraphQL API is up and running. 11:59 Let's go over to our Gatsby sites 12:03 and start that up 12:05 with a Gatsby develop. 12:08 This is going to build 12:12 the static content 12:16 for our Gatsby site. 12:19 Start a development server 12:21 so we can take a look at it. 12:23 So that's running on localhost 8,000. 12:28 Here's our blog starter. 12:31 And then it also starts the Gatsby-GraphQL API 12:36 on 8,000/___GraphQL. 12:44 And we can make sure that we're polling in 12:47 our Neo4j-GraphQL API 12:51 using the gatsby-source-graphql plugin. 12:59 Cool. 13:03 Okay. So now we're ready to change 13:09 some of the content on this page. 13:12 And what I wanna take a look at... 13:16 So for now I'm gonna close these files. 13:20 And what I wanna take a look at 13:21 is the way that Gatsby uses GraphQL 13:26 to populate content 13:29 in the pages that Gatsby creates. 13:31 And there's a few different ways 13:34 that Gatsby does that. 13:39 Let's take a look at one way 13:44 which is using a StaticQuery component. 13:52 So if we take a look at this Index page, 13:58 the page that's listing title of the site, bio, 14:05 and then our blog post. 14:07 That is going to be in source, 14:13 pages, 14:14 index. 14:20 So this is the react component 14:24 responsible for rendering this page. 14:29 And if we see here, there's this bio component 14:33 that's being imported from components bio. 14:39 Let's take a look at that. 14:44 And here we see this bio component 14:47 is polling in this useStaticQuery hook 14:54 and running a GraphQL query, 14:58 and then rendering some content 15:01 using the results of that GraphQL query. 15:05 So this is the first way that Gatsby can use GraphQL, 15:09 is with this useStaticQuery. 15:13 Here's a hook. 15:14 There's also another component. 15:18 StaticQuery component that uses a different React API. 15:24 But this hook is the more, 15:27 is the more now idiomatic way 15:32 to work with React state. 15:34 Anyway, 15:35 this is the first way that Gatsby uses GraphQL data. 15:39 So basically in any component, 15:41 we can poll in this useStaticQuery hook, 15:43 give it a GraphQL query, 15:44 and then render some contents. 15:49 Cool. 15:51 So let's change this content a bit. 15:57 So if we look at the GraphQL query, 16:00 it's hitting the site query field, 16:05 getting some metadata. 16:11 So where is that coming from? 16:13 Well, if we take a look at gatsby.config, 16:21 we can see where that siteMetadata is being set 16:25 in this object. 16:27 So let's change this. 16:31 So the title is... 16:36 What did we call this? Central Perk. 16:40 The author, 16:46 we can use myself as the author. 16:50 That seems fine. 16:56 Description of the site. 16:58 This is a travel guide to Central Perk 17:08 using Neo4j, Gatsby and GraphQL. 17:20 I've got some social stuff. 17:23 We don't have a URL yet. 17:24 So we'll leave that as is. 17:30 Cool. Okay. 17:31 So if we go back now to our index 17:39 or rather what do we want. 17:41 We want the bio. 17:42 Go back to the bio component 17:43 and take a look at the GraphQL query 17:45 that's used to populate this. 17:47 'Cause we changed that data in the source. 17:52 We can see its changing here. 17:54 Instead of though this author information 17:58 Instead I wanna just show the description 18:04 for the site. 18:04 So if you go to the GraphQL API, 18:09 we're showing the site query field 18:12 and then site metadata. 18:17 We have what, the title, the author. 18:19 I don't really care about a description. 18:21 That's what I'm interested in though. 18:24 I don't want that description in field. 18:27 So let's add that 18:32 to our GraphQL query description. 18:36 And then 18:41 let's just skip rendering this image. 18:43 We'll fill in a Central Perk logo or something later, 18:49 and then here written by and the author summary. 18:54 Yeah, I don't really care about that. 18:57 Instead data.site.description 19:02 is what we want to render. 19:18 data.site.siteMetadata. 19:23 So need to follow the selection set of our GraphQL query. 19:27 So the result of the query 19:34 is parsed in to our bio component 19:38 as data props, 19:41 the data prop. 19:44 Okay go in author, social 19:46 and let's grab a description here. 19:50 So we can just say description. 19:56 Okay. 19:57 That, and we don't need to link to put it. 20:06 So we can remove that too. 20:15 Okay, cool. 20:16 So we've seen how to use 20:20 this useStaticQuery hook to poll in content 20:24 from the Gatsby-GraphQL API 20:27 and render that in our React component. 20:31 Let's take a look at the second way 20:35 that Gatsby uses GraphQL. 20:39 And we'll find that in the Gatsby node file. 20:50 So here we can look at the Gatsby documentation 20:55 for the Gatsby Create Pages API. 21:04 I guess that's what we want, 21:14 Right. So here we're using the Create Pages API in Gatsby, 21:26 to query for here all of the blog posts 21:34 that have been in this case, 21:37 created as markdown files. 21:40 So it's using a plugin to parse those markdown files. 21:47 And that's what here we're showing one post 21:51 for each one of these markdown files. 21:54 In this case ordered by date. 21:59 And then for each one of those blog posts 22:03 that comes back from that GraphQL query, 22:06 we're calling createPage. 22:09 Defining the path as the slug. 22:15 And here let's run this query 22:17 so we can see actually what we're getting back, 22:32 Right? So basically we're getting back for each post, 22:38 a slug. 22:40 That's going to be a component of the URL 22:44 and the title of the blog post. 22:48 So when we call, createPage, 22:50 we're saying, "Hey, create a page in Gatsby, please." 22:55 The path is just going to be the slug. 23:01 We're going to render this using the blog post component. 23:06 So we'll take a look at that in a second. 23:07 That's just being imported here 23:11 from source/templates/blogpost. 23:12 That's just a React component. 23:15 And in the context object. 23:16 So what else do we wanna parse into the blog post component 23:21 when it's being rendered? 23:22 Well, we wanna give it the slug 23:24 and this previous and next, 23:27 which here we're just finding the previous and next post 23:32 here in this array that comes back from the GraphQL query. 23:41 Cool. So we create those pages 23:46 and then they get rendered by this blog post component. 23:52 So that's the second way to use GraphQL and Gatsby 23:57 is with this createPage API. 24:02 And what we're going to do 24:03 is we're going to modify this 24:08 to create a page 24:14 for each Point Of Interest. 24:18 In our case, so rather than using content for markdown, 24:23 we wanna use content that comes from Neo4j. 24:26 In this case each one of these Points Of Interest, 24:28 we wanna create a page 24:29 that will then have some detailed information 24:31 about that Point Of Interest. 24:32 Eventually we'll have a map that shows where it is. 24:35 We'll find some photos. 24:36 See If we can poll in some other content along the way. 24:45 Before we modify this page 24:50 and get into the WITS, 24:51 let's just take a look at the third way 24:54 that Gatsby can use GraphQL. 24:58 And for that, 24:59 I'm going to look at this Index page. 25:04 So within pages we have a 404 and an index. 25:10 So index.js, 25:11 that's let this page, 25:14 which we looked at briefly, 25:16 you were sort of just going down here to the bio component. 25:20 So we saw it in the bio component. 25:22 It's using the useStaticQuery hook in that component 25:29 to query the GraphQL API, 25:31 the Gatsby is serving. 25:34 Another thing that we can do 25:37 is export a page query. 25:42 And in that case, 25:43 that page query is a GraphQL query 25:47 that defines content that we wanna then parse into, 25:52 in this case, the index page. 25:54 And that comes in the data prop 25:59 into our, what's essentially our page component 26:02 in this case called blog index. 26:07 So here we've defined, 26:13 let's run this here. 26:15 Here we've defined the query 26:17 that's going to populate 26:19 the data that renders this index page. 26:25 Here we can run that in GraphiQL 26:28 to see, yep, we get the siteMetadata. 26:31 Then we're using that markdown plugin 26:37 to parse our markdown files. 26:39 And in this case we're grabbing 26:42 the title, the date 26:45 and an excerpt and a description for each post 26:51 which we're showing here. 26:54 So what we'll do is we'll modify this 26:57 to show every Point Of Interest. 27:07 I think what we'll want eventually 27:10 is maybe just render a map of Central Perk 27:12 and we'll just render these Points Of Interest 27:15 as like points on the map. 27:16 And we can sort of like click in and explore it that way. 27:20 But for now, I just wanna render like one of these links 27:25 to each of the detailed posts for each Point Of Interest. 27:29 So we'll take a look at that in a second. 27:34 Hey Mustapha. Thanks for joining. 27:38 Glad you're liking this tutorial school. 27:44 Okay. So gatsby-config, 27:47 we looked at bio, 27:49 we looked at index. 27:50 We'll come back to... 27:52 What we wanna do is jump back to gatsby-node. 27:55 And here let's change this page 27:58 to tell Gatsby to create a page 28:03 for each Point Of Interest. 28:08 So let's first start off by changing this query, 28:13 Switch to GraphiQL. 28:16 And first let's grab all of our Points Of Interest. 28:20 Let's just grab them all from the database. 28:21 So we'll start off at the POI field. 28:25 Remember that's in gatsby-config. 28:30 That's the field that we said we wanted to sort of mount 28:37 our Neo4j-GraphQL API 28:40 under the POI field. 28:44 So that's where that comes from. 28:47 So it's sort of a namespace, POI, 28:50 and then we want every Point Of Interest 28:52 and for every Point Of Interest 28:53 we want the name, 28:58 the type, 28:59 what else do we have? 29:01 Oh, right. We have... 29:03 So here we can do some routing. 29:04 We won't use that today. 29:06 The tags probably don't wanna display that, 29:11 or we probably don't need that 29:15 for just creating the pages. 29:20 We do probably however need the node_osm_id. 29:26 So this is what we'll treat as the slug. 29:30 So we'll use this as part of the URL. 29:34 So every Point Of Interest has a unique ID, 29:37 which is this node_osm_id field. 29:43 And you see here we get an error in Gatsby 29:48 and the Gatsby-GraphQL API 29:51 that says it's cannot be represented, 29:53 can not represent non 32-bit signed integer value. 30:00 If we go to the Neo4j-GraphQL API directly 30:10 and query node_osm_id 30:13 we see that we can get those values. 30:15 And this is a bit interesting. 30:18 So the GraphQL spec 30:21 is actually a bit vague about handling, 30:30 in this case, these would be longs. 30:33 So these are 64-bits integers rather than 32. 30:41 It sort of depends on the system, 30:44 whether or not you want to be able to handle 30:46 those long values as integers. 30:51 Looks like Gatsby doesn't let us do that. 30:55 So let's change this 30:58 in the Neo4j-GraphQL API. 31:03 We'll jump over there for type definitions. 31:09 We defined the node_osm_ID as a integer field. 31:15 And this actually works out better. 31:16 Let's explicitly say, "Hey, this is an ID required fields." 31:25 And then in the database, 31:31 actually, I'm not sure if we need 31:32 to actually change those two strings 31:36 or if GraphQL just sort of treats those IDs and strings. 31:41 Let's refresh. 31:47 Yeah. So treat that as a string. 31:49 Okay. So now I think we need to restart Gatsby 32:01 to pick up that change. 32:05 There, Gatsby develop. 32:19 There we go. 32:21 So now, because we said those are ID fields, 32:27 they're sort of treated as strings 32:34 for serialization. 32:38 Okay, cool. 32:38 So back to Gatsby site, 32:44 Gatsby node. 32:47 So to create each page, 32:49 really all we need is the name 32:54 and the node_osm_id. 33:00 So we'll replace that query here. 33:11 Okay. So then, 33:15 posts is not gonna be 33:17 result.data.alMarkdownRemark.edges. 33:21 Instead it's going to be data 33:24 and seeing the result here falling in this shape. 33:26 So data.poi.PointOfInterest. 33:32 So that's gonna be this array of objects. 33:37 And then we do a, forEach. 33:39 So for each post, 33:42 defining the previous and the next, 33:45 and then calling .node. 33:47 Well we don't need that .node. 33:48 The previous and the next 33:49 is just gonna be the object. 33:52 So we don't need to call .node here or here. 33:59 And then when we call createPage, 34:00 the path is gonna be just the post.node_osm_ID 34:11 node_osm_ID. 34:16 Component. We'll still use this blog post component 34:18 to render. 34:19 We'll need to make some changes. 34:21 And then this context object, the slug, 34:25 is going to be node_osm_id, 34:30 still the previous and the next here. 34:31 So that's... 34:34 I don't know, we may have broken this. 34:37 I know it still shows that. 34:38 So that's here the previous and next. 34:42 So we can sort of paginate 34:43 through our points of interest blog posts. 34:50 Okay. And then we have this uncreate node handler 34:54 that is using the create file path 35:00 from a file system plugin 35:02 which was gonna comment out. 35:04 So I don't think we need to worry about that. 35:09 Okay. That I think is gonna break a bunch of things. 35:21 Yeah. 35:26 That was an error in index 35:30 Yes, that has broken. 35:32 So we need to fix that. 35:34 But let's first fix 35:37 the blog post component. 35:43 So now we're saying, 35:45 okay, I wanna create a page in Gatsby 35:48 for every Point Of Interest that comes back. 35:51 So for every one of these, 35:52 I wanna create a page. 35:54 And I want the URL for that page 35:58 to just be / 36:08 /the osm_node_id. 36:10 So this scheme here. 36:14 And to render that page, 36:17 I want to use this source templates blog-posts component. 36:23 So let's take a look at that. 36:25 So source, 36:28 templates, 36:29 blog post. 36:33 And if we take a look at this, 36:35 we can see it's defining a page query. 36:40 In this case, this component is past the slug. 36:49 So it's then using that slug 36:51 to look up the detailed information 36:56 about that blog post. 37:02 I can run this. 37:11 And instead of using... 37:12 I'm going to find the variable in here, I guess. 37:20 Slug, 37:24 you know it's a one new-beginnings. 37:32 No one expected, 37:40 no one expected token. 37:48 Very very long slug. 37:54 (clicks) 37:59 Our GraphQL API is not running 38:01 'cause we broke some things. 38:03 Can we run? Can we get it back? 38:04 Gatsby develop. 38:09 So I think when we make changes 38:11 to gatsby-node.js 38:14 we need to reload 38:18 the Gatsby build. 38:21 And I know we broke some things. 38:22 That's fine, 38:24 Complain, but do we have the GraphiQL back? 38:27 Yes we do. 38:30 And then arguments fields. 38:35 All right. Because we were no longer populating 38:40 that in Gatsby node when you create the page. 38:43 Okay. That's fine. 38:46 We broke that, but that's okay. 38:50 We're gonna replace that query anyway. 38:52 I just wanted to show that this... 38:54 what this GraphQL query returns 38:57 like basically it's giving us the content for the blog post, 39:03 but we're going to replace that with our own queries. 39:08 So what's that gonna look like? 39:09 Well, we know we wanna use this slug value 39:12 to look up a Point Of Interest 39:16 by node_osm_id. 39:25 Let's just hard-code one in here. 39:28 Let's grab one from the database. 39:32 So the Point Of Interest for... 39:37 Oooh! 39:39 The pool. 39:43 Is that an actual swimming pool? 39:44 I don't know. 39:47 Okay. Here's a node_osm_id. 39:53 So let's just stick that in there for now. 39:56 So for Point Of Interest, 40:00 what are we gonna want? 40:00 We're gonna want as much detailed information 40:03 as we have to render. 40:05 So we're gonna want the name, 40:08 the location, latitude and longitude. 40:10 We wanna show that on a map, 40:12 we have some tags, 40:14 we have the type of Point Of Interest. 40:19 We don't wanna include the routing. 40:20 So... 40:28 Aah! 40:31 So the trouble there 40:32 is it's treating this as a string 40:35 because we said those were ID fields. 40:41 So let's change those in the database 40:44 from integers to strings. 40:47 So for every Point Of Interest, 40:50 we're gonna set p.node_osm_id 40:56 equal to, cast those to a string. 40:59 P.nod_osm_id. 41:02 Yeah. For every Point Of Interest 41:05 set the value of the nod_osm_id property 41:12 to that property cast to a string. 41:15 That should work. 41:24 Okay. Now I have my very detailed information here 41:31 that we got from OpenStreetMap. 41:34 You don't have any tags for this one 41:36 that tells us that the pool is water. 41:38 And okay. Great. 41:40 Very helpful. 41:41 Okay. So, that's kind of all of the data we have for now. 41:48 Oh, let's... 41:51 Right. 41:52 Let's instead of hard-coding this 41:55 use the GraphQL variables syntax. 42:07 So we need to name the query or something. 42:12 How about POIBySlug. 42:18 And this is going to take the GraphQL variable called slug, 42:25 which is of type non-nullable ID. 42:30 And we're gonna use it here 42:33 in that field argument. 42:46 Switch back to new-beginnings. 42:49 We don't want that. 42:51 There we go. Okay. 42:54 So we can copy this. 42:56 Drop this in here. 43:02 Okay. So now when you render a blog post page. 43:07 We get parsed in the slug 43:13 because we set that in the context object here, 43:18 one we called createPage. 43:20 So though if you're wondering, that's where the value 43:23 for that slug variable comes from. 43:32 Okay. So what we need to do now 43:35 is update this to render data 43:40 based on data that we actually have. 43:42 So we have name, type, tags and location. 43:46 So let's modify this. 43:47 So the post is going to be, 43:51 non data.markdownRemark. 43:54 We have to follow this structure 43:56 based on the selection set of our GraphQL query. 44:00 So data.poi.PointOfInterest, 44:06 and this, this is an array 44:09 because this could potentially match, 44:14 even though we're using an ID here, 44:16 the generated query fields 44:20 in Neo4j-GraphQL-js. 44:24 Don't know that we're using an ID field 44:26 so treats that as something that could return an array 44:30 of Points Of Interest. 44:34 So let's have the first entry. 44:38 We should usually only just have one 44:40 because it's a unique ID. 44:43 This node_osm_id value is a unique value. 44:46 Okay. On the site title that should be fine. 44:49 Previous and next that should still be fine. 44:53 Location should be the same. 44:54 Title should be the same. 44:56 This SEO component. 44:57 I'm guessing that setting like the meta tags. 45:02 Instead of post.frontmatter.title. 45:05 So post now represents this object. 45:08 So when refer to post, 45:11 we're talking about this object. 45:14 So (mumbles) 45:16 post.name 45:17 Description, 45:21 post.type. 45:24 Then we've got a header, 45:28 post.name 45:30 we'll use for the header. 45:36 Then we show the date. 45:40 We don't have a date. 45:41 We can show post.node, 45:44 to show the ID. 45:49 And then here, 45:50 this is where it's setting the HTML content 45:57 for the blog post that comes back from that markdown plugin, 46:01 which is quite nice. 46:02 But we don't have that. 46:04 We do have a paragraph. 46:10 We do have 46:15 post.tags that we can show. 46:21 Which is an array of strings. 46:29 So the tags are kind of hokey for now, 46:31 we'll fix that in a second. 46:33 The tags right now, 46:36 if you look at our GraphQL API, 46:38 tags is an array of strings. 46:41 We don't have them for everyone. 46:43 And right now it's just the keys 46:46 cause the... 46:46 These tag nodes 46:49 have kind of arbitrary key-value pairs 46:51 from OpenStreetMap. 46:53 So for now we just polled in the keys. 46:58 So because it's an array 46:59 we can, add it .join. 47:03 Just to add 47:05 from comments between them. 47:09 And its an optional. 47:14 So we need to make sure we don't try to call .join 47:20 when it's undefined. 47:24 Okay, cool. 47:25 And then the footer 47:26 we include this bio component. 47:30 That should be fine. 47:32 And then here's where we're linking 47:35 to the previous and next. 47:39 And the link is going to be 47:43 just previous.osm_node_id 47:49 and previous.name. 47:56 next.osm_node_id, 48:02 and next.name. 48:10 Okay. I think 48:13 that should be good. 48:13 What, you see any errors? 48:17 Not on this page. 48:22 So Gatsby is restarting. 48:28 Is it restarting? 48:35 Maybe we need to fix that error in index 48:39 before we can render one of these, perhaps. 48:48 I'm not sure if we're not able to render the index page. 48:52 I'm not sure if it will still build all the other pages, 48:55 but we'll find out in a second here. 49:11 I think no. 49:13 Okay. So let's fix that next. 49:14 So, 49:17 I think we got everything in our blog post template. 49:24 We jumped over to, nope, not that index. 49:29 Pages, 49:30 index and Gatsby. 49:35 Okay. So here, remember this, 49:38 this component is exporting a page query. 49:42 Which means that when this page gets built, 49:46 this GraphQL query is going to run. 49:48 And then results of that will be parsed in the component 49:51 as the data prop. 49:53 Then we can use the results of that 49:57 in the page component. 50:02 So this query, 50:04 this is gonna be pretty similar 50:06 to the query that we ran 50:10 to get all of the pages to build. 50:15 So it's going to be POI Point Of Interest. 50:21 I guess the difference is we'll bring in 50:25 what we need to one to link 50:30 to the details to the blog posts. 50:33 So in this case, 50:35 we're treating that slug as the node_osm_id. 50:38 So we'll need to include that 50:41 in our GraphQL query. 50:42 And then we'll need to include the name 50:47 and the type. 50:52 I think that's all that we're going to need for now. 51:00 Here they're ordering this by date of the blog posts, 51:04 but we don't really have anything to order it by here, 51:09 since we're describing all Points Of Interest. 51:11 I guess since we know what we really wanna do 51:14 is show these as markers on a map. 51:19 Let's just go ahead and include latitude and longitude 51:22 in the query now, for all of these. 51:24 It's really what we wanna do 51:25 instead of just showing a long list of these, 51:27 is show a map that we can click on 51:30 and get more information on. 51:37 Okay. So we run that query 51:40 and now in the data prop, 51:46 Oh, we also need to include site title. 51:52 That's a good point. 51:53 So siteMetadata title, 52:01 do we use any other sites? 52:04 Site title, just site title. 52:06 Okay, cool. 52:09 So include that query field. 52:17 Okay. So site title posts are going to be 52:26 data.poi.PointOfInterest. 52:32 So this array again. 52:37 Site title is fine. 52:39 SEO. That looks fine. 52:41 Bio that's fine. 52:43 And then map over all of our posts. 52:51 We don't have a node, 52:55 instead guess we just want that object. 53:00 So we can call it node. 53:05 That's fine. 53:06 So basically map over all of our posts. 53:11 Node becomes the value of each one of these post objects. 53:18 The title is node.name. 53:26 Key. 53:28 So in React, when we do these map operations, 53:31 where we're generating elements, 53:35 that we're iterating over an array or mapping over an array 53:41 and creating an element for each item in the array, 53:46 reactively wants a unique key 53:48 for each one of those elements. 53:50 It can identify for performance reasons 53:53 as it renders the shadow Dom that it has. 53:58 So this we can use node.node_osm_id. 54:05 That should be a unique value. 54:08 Then in the header 54:11 we link to... 54:13 So what we're doing now 54:15 since we don't have our index page 54:16 what we're doing now is showing 54:18 for all the Points Of Interest, 54:20 here's the name of the Point Of Interest 54:21 as a link that we can click on 54:23 to go to the detailed page. 54:27 So each one of the link 54:29 is going to link to the node_osm_id 54:39 and title, yup. 54:40 So name of the Point Of Interest. 54:44 And then we show the date. 54:51 I think let's just show the name of the Point Of Interest. 54:53 So we'll delete that. 54:55 And then we'll also delete the section here 54:57 where it's showing an excerpt from the blog posts. 55:05 Okay. I think we should have something to look at now? 55:11 Cool. So now, 55:14 some reason we have a lot of Points Of Interest 55:17 called pitch. 55:18 I have to look at that. 55:19 But... 55:20 okay. Now we have this big long list of Points Of Interest 55:25 from our Neo4j database. 55:29 And if I click on one of these 55:32 takes me to a detailed page. 55:37 Looks like we have an error in our blog post template. 55:40 So let's figure that out. 55:47 Cannot read siteMetadata of undefined. 55:52 So it's trying to show us the title. 55:58 Oh, well, we don't include that. 55:59 We need to include that in our GraphQL query. 56:04 So that is siteMetadata title. 56:27 What did we do wrong there? 56:28 Site, Oh, it's a lowercase. 56:30 Yeah. Like that. 56:37 That's rebuilding our pages now. 56:57 Okay. And if we refresh. 57:01 Cool. So now, 57:06 we can view all of the Points Of Interest in a list. 57:13 We can click on one to get detailed information. 57:17 We don't have a whole lot of detailed information. 57:20 We have the tags. 57:24 We also wanna show a point on a map using the location. 57:28 We'll get to that. 57:29 But then we have this next link 57:35 that we can click to. 57:40 Why doesn't that work? 57:42 That's not something I can click on. 57:46 What's going on there? 57:47 So link. 57:55 Is that not? 58:00 So that's the link 58:05 previous.node_osm_id 58:09 Doesn't have a href. 58:15 Why is that? 58:23 node_osm_id 58:29 Oh, it's node 58:32 node_osm_id. 58:36 Not osm_node_id. 58:48 Let's try that again. 58:50 Cool. Oh, and we've appended. 58:53 So we want the link to be, 58:57 I guess, root. 59:02 So I'll say, / 59:05 and then concatenate the node_osm_id. 59:10 So we were adding that to the current URL. 59:15 Just not what we wanted. 59:19 So when that rebuilds, we'll make sure that works 59:22 and then we'll see what we wanna do next. 59:31 Okay, cool. 59:31 So now I can paginate through these. 59:38 Cool. 59:40 Now, you can see some of these, 59:41 we don't have tag information for. 59:44 So, I have to find other sources of add info. 59:50 Okay. So there's more data in the database here. 59:53 These tags, 59:56 we're just bringing in the keys. 59:59 So there's a value here for artist's name, type of artwork. 60:03 Some of these link to the Wikipedia pages. 60:06 So this Robert Burns statue 60:09 has its own Wikipedia page. 60:12 Okay. Let's see if we can find it. 60:16 If you go to Wikipedia, 60:21 Robert Burns, Central Perk statue 60:30 A bronze portrait statue in New York City. Yes. 60:38 Cool. So, 60:41 so these are the kind of things 60:43 that I wanna explore, 60:45 polling into our GraphQL API. 60:47 So, because we know 60:51 there's gonna be a Wikidata ID. 60:55 So we've created a Wikidata API with the ID 60:58 or this I think is gonna be the ID for the Wikipedia page 61:03 that has this information. 61:05 Which this is all stuff we wanna include 61:07 in our travel guide. 61:08 So we'll see how we can include that in as well. 61:11 But for now, what we need to fix 61:14 is we look at what we're returning for tags 61:19 from our Neo4j-GraphQL API. 61:20 It's just a list of strings 61:21 that are the keys for all the tags. 61:25 So let's return the keys and the values 61:30 so we can display a little bit more information. 61:33 Okay. So not in Gatsby site, 61:36 if we go to Neo4j-GraphQL.js index JS, 61:40 and if we take a look at our schema. 61:46 Here's our Point Of Interest type. 61:47 So with Neo4j-GraphQL.js 61:50 the types that we define 61:52 in our GraphQL schema map to... 61:55 that's a bit too big, I think, 61:57 Map to node labels 62:00 and properties in the database. 62:03 So name, location, type, node_osm_id, 62:07 those are all properties that are start on the node. 62:11 Then we... 62:14 We can also traverse relationships 62:17 using a @relation directive. 62:22 Let's take a look at the database. 62:24 So this is more clear. 62:25 So let's grab a Point Of Interest. 62:32 And if we traverse out from Walter Scott, we can see here, 62:37 there's this Walter Scott Point Of Interest node 62:43 has a outgoing tags relationship to an OSM tags node. 62:48 And if we click on this note and look at the properties, 62:52 we can see here that we have these arbitrary properties. 62:59 So artists name, artwork type, 63:02 name, date, tourism, those kinds of things. 63:06 Those values are the tags that come from OpenStreetMap 63:10 and they're kind of arbitrary. 63:12 They're not defined. 63:13 So if you look at it, they're not defined ahead of time. 63:15 So if you look at the pond, 63:18 you can see that its tags are different. 63:25 So these tags depend on what sort of Point Of Interest 63:30 we're working with. 63:31 So we could, in our schema 63:36 define something like this, 63:38 where we say, 63:42 the tags filled on Point Of Interest 63:45 is an array of tag objects. 63:54 And it is defined 64:02 like this. 64:04 And then we define a tag type. 64:11 So we could do this. 64:12 And then Neo4j-GraphQL.js would know, 64:14 "Oh, okay. So you're on a Point Of Interest. 64:17 You have a tags, 64:20 which is a field 64:23 that is defined using this @relation directive. 64:26 So I'm going to traverse out 64:29 along outgoing tags relationships to get to this. 64:32 Yeah. Well, this would be an OSM tag type 64:36 since that matches the OSM tags label on the node. 64:43 Okay. So I'm gonna go that traverse this tags relationship 64:46 to find this OSM tags node. 64:50 But then that's where we run into some problems 64:51 because GraphQL has strict-type safety. 64:56 We kinda need to know the property values. 65:00 What are the keys of all the properties? 65:04 And define those as fields on the type. 65:07 So we don't wanna do that cause we don't really know 65:09 what those are going to be. 65:12 So instead, what we've done is define the tags field, 65:18 using a cypher schema directive. 65:22 So the cypher directive allows us to bind this field 65:27 to a cypher statement. 65:31 So now when we are at the Walter Scott Point Of Interest, 65:35 if I request the tags field, 65:37 we execute this cypher statement, 65:40 which in this case says, 65:42 "traverse out to a OSM tags node 65:46 and return just the keys. 65:52 So the keys of all of these properties. 65:54 So right now we're not getting the values. 65:57 So let's fix that. 65:58 But we said that they're kind of arbitrary. 66:00 So how can we represent that in GraphQL? 66:04 Well, What we'll do, 66:05 we'll create a type tag 66:08 that has a key field 66:14 and a value field. 66:18 And we can also define computed object fields 66:24 using the cypher schema directive. 66:28 Here we would just find a computed scalar field. 66:32 But we can do object fields as well. 66:39 It looks like this... 66:41 Looks like we have multiple relationship types in the data. 66:44 So we go from a Point Of Interest 66:46 along a tags or along an associated. 66:49 So I'm just gonna take the relationship type out of here. 66:54 So basically follow any relationship type 66:57 from a Point Of Interest 67:00 to an OSM tags node. 67:04 And then, and just instead of returning the keys, 67:07 what I'm gonna do is unwind over the keys. 67:15 So iterate over that list of property keys. 67:22 And I'm gonna return an object. 67:28 the value of the key property 67:33 is whatever that key is. 67:36 So it could be artist's name, artwork type, et cetera. 67:39 And then the value 67:42 is the value of the property. 67:52 And you can alias that to something, 67:53 it doesn't really matter, 67:54 but that looks nicer. 67:58 Okay. So we'll save that, make sure it restarted. 68:02 It did. 68:04 Let's see if that worked. 68:05 So now tags is an object field, 68:11 so we need to grab the key 68:15 and the value. 68:21 Cool. So now we have, 68:24 before the tags field, 68:27 we have a list of key-value pairs, 68:31 but some more detailed information now that we can include. 68:35 And because previously I was just following 68:40 this tags relationship, 68:42 but we also have these associated relationships. 68:44 So now it looks like we're getting some data 68:46 where we had nulls before. 68:48 So that's good. 68:51 Cool. So a question from the chat. 68:53 In the case, the tag type would not be navigable, right? 69:00 Assuming later on you'd like to find other 69:02 related objects to tag. 69:03 Yeah. That's a really good point. 69:05 So, 69:08 let's say that we wanted something like 69:19 POI's Point Of interest. 69:27 Let's say we wanted something like this on the tag type, 69:32 where we now wanna go from any of these Points Of interest 69:37 that we've returned 69:39 for any of the tags that we found. 69:42 Now we wanna find other nodes 69:44 that have the same tag. 69:50 Well, because of the way that we've defined this tag 69:56 using the cypher directive, 70:01 we don't really get that behavior out of the box. 70:06 So if we were using a relation directive 70:10 to go to all those OSM tag nodes, 70:12 we'd do something like this. 70:19 And then Neo4j-GraphQL.js should be smart enough 70:22 to figure out the query to do that. 70:28 But because we're sort of projecting that object 70:33 using these cypher statements, 70:35 we don't get now that this traversal behavior, 70:39 as part of Neo4j-GraphicQL.js 70:43 I think we could define a custom resolver 70:50 that would then do this part of the traversal for us. 70:56 I think that should work. 70:58 But in general, yeah, 71:00 that's where things get kind of messy. 71:03 I try not to use cypher directives 71:08 to return object fields. 71:13 If I want to then do a nested selection 71:17 on that object field 71:19 here, tag is just a sort of simple container 71:24 to hold key-value data. 71:27 I'm not really thinking of it as representing 71:31 an OSM tags node in the database. 71:35 So yeah, that's a good point that you bring up. 71:39 This is kind of a special case 71:41 because we wanna be able to get around 71:48 the rigid GraphQL-type system 71:52 using the sort of arbitrary key-value pair 71:57 representation of our times. 72:01 Yeah. Good point. 72:11 I realize now is more general question 72:13 introspection and objects. 72:16 Right, what you're saying? 72:19 Yeah. So I think the, you know, 72:23 the question comes up a lot 72:25 or at least I see this in some schemas, 72:29 where we're using cypher directive fields, 72:33 when we really should be using @relation fields. 72:38 And if it's sort of a natural traversal 72:43 where you're traversing a relationship, 72:44 you typically just wanna use those @relation fields. 72:48 There's a little bit, I think, in the documentation 72:51 about these docs. 73:00 ♪ Aaah mmh tu tu tu ♪ 73:02 Okay (mumbles) 73:06 What am I looking for? 73:07 The guides, 73:11 this one, 73:12 schema design. 73:14 So this page talks a little bit about 73:16 when we should use relation directives 73:21 and when we should use cypher directives. 73:27 Yeah. Anytime we have custom-computed logic, 73:32 that's when you wanna use the cypher directive, 73:34 if it's just following a traversal of the graph, 73:40 you should define those typically with relation directives. 73:47 Okay. So let's see. 73:53 Yup. Cool. That works. 73:54 Okay. So now what we wanna do is go back to Gatsby 74:00 and anywhere that we polled in our tags, 74:05 instead of showing this list of tags, 74:10 the tag keys, 74:11 now we can show the actual detailed information, 74:16 the values for these tags. 74:20 So that's going to be in the blog post template. 74:29 So the first thing we need to do 74:30 is update our GraphQL query. 74:33 Tags is now not a scaler. 74:37 It's an object that has key and value fields. 74:43 And, 74:47 here's where we're showing them. 74:51 So instead of just joining the array together, 74:59 let's create another request. 75:07 And, 75:10 what can we do here? 75:11 Maybe map over these, I guess, 75:17 so, 75:21 post.tags 75:25 this is an optional field. 75:31 So map over this array 75:36 and return a list item 75:42 that has the key 75:50 and the value. 75:58 Oh, I think we want our key here can be the index. 76:02 So every, again, because we're iterating, 76:05 we're mapping over an array, 76:07 generating elements. 76:09 We should add a key prop 76:12 that has a unique value. 76:17 Okay. 76:18 And it looks like, 76:21 looks like Gatsby needs a restart to pick up the changes 76:25 to our GraphQL API. 76:28 So let's restart that. 76:35 Look at them error, 76:38 tags must not have a selection. 76:41 So it still doesn't like 76:47 let's refresh GraphiQL. 76:49 It still doesn't like this. 76:58 Suddenly it won't pick up the change 77:01 to our GraphQL API. 77:05 I think Gatsby maybe caches some of this. 77:08 So I think, do we do Gatsby cleaning maybe? 77:15 Yeah, there we go. 77:16 So that deleted the cache. 77:18 So now if we do Gatsby develop, that will rebuild. 77:22 So now I think it will look at our GraphiQL... 77:25 our Neo4j-GRaphQL API, 77:28 do an introspection query 77:30 to pick up the updates there. 77:36 Let that run to rebuild. 77:43 While we're waiting. 77:45 Let's add... 77:49 Let's make the key, bold. 77:52 It gets to stand out a bit more. 77:54 Well, eventually we'll have to take a look 77:58 at CSS and styling, 78:01 but for now we'll just bold that. 78:06 Okay. And let's restart. 78:10 You go to a detailed page. 78:11 Cool. Here we go. 78:12 Now, 78:15 if we go to our detailed information. 78:24 Some of this looks good. 78:29 Object, object, object. 78:30 What is that? 78:32 So we're getting some detailed information here. 78:37 So we can tell that the Fitz-Greene Halleck, 78:42 the statue created in 1877, 78:46 it has a Wikipedia page. 78:48 That's great. 78:51 But then what's this object, object objects stuff. 78:57 Oh, 'cause I still have this here. 78:58 Or a join 79:00 Yeah. So I'm still calling join on post.tags. 79:06 But now because tags is an object, 79:10 I get that object representation of it, 79:18 which is not very useful. 79:19 So let's remove that. 79:25 Okay, cool. 79:28 So we've got some detailed information. 79:33 Remember we also have location data here. 79:35 So we have latitude and longitude 79:37 for each one of these. 79:40 Pitch. 79:42 To figure out what these... 79:43 what the pitch thing is. 79:45 Oh, is this a bunch of tennis courts? 79:46 Maybe this is just a bunch of individual tennis courts, 79:50 then imagine this 79:52 there, baseball fields. 79:57 Cool. 79:59 I think that is probably a good place to stop for today. 80:03 So we made some progress. 80:06 I will push this code up to get hub 80:09 and the recording for this, 80:11 and I will be linked in that README as well. 80:15 So you can follow along 80:17 just to kind of recap what we did today. 80:22 So we looked at the different ways 80:26 that Gatsby uses GraphQL. 80:29 So we looked at the useStaticQuery hook. 80:35 We looked at the Create Page API. 80:40 And we looked at the page query, 80:44 for example this that we used in the blog post template. 80:48 The page query method, 80:49 where page exports GraphQL query, 80:52 and then the results are parsed in to the data prop.. 80:57 So we looked at the different ways 80:58 the Gatsby uses GraphQL 81:00 to render content. 81:03 And then we populated our Gatsby sites 81:08 with data from Neo4j 81:11 from the Neo4j-GraphQL API. 81:14 And we updated that Neo4j-GraphQL API a little bit 81:19 to include more detailed information on tags. 81:25 Cool. So that's it for today. 81:27 Next week, I think maybe we'll take a look 81:30 at how we can fetch some more of this detailed information. 81:34 So for example, 81:36 where we have Wikipedia and Wikidata links. 81:41 Can we pull that content 81:42 and as well and render that. 81:47 We've looked at polling in images 81:52 and look at showing a map 81:56 with these different Points Of Interest. 82:01 And then after that, we'll take a look at routing. 82:04 So how can we route 82:06 from one of these Points Of Interest to another? 82:10 But that will be for future sessions. 82:13 So thanks everyone for joining today 82:16 and hope you can join again next week. 82:19 So I'll see you then. 82:21 Cheers.
Subscribe To Will's Newsletter
Want to know when the next blog post or video is published? Subscribe now!