Maps & Routes With Mapbox GL JS
Building A Travel Guide With Gatsby, Neo4j, & GraphQL: Part 6
Adding a map with Mapbox GL JS and React. Displaying routes using Neo4j Graph Data Science pathfinding algorithms. Using Apollo Client in Gatsby.
Links And Resources#
- The code on Github
- OpenStreetMap Neo4j Sandbox
- NODES 2020 Online Conference
- GRANDstack documentation: designing your GraphQL schema
- Five Common GraphQL Problems and How Neo4j-GraphQL Aims To Solve Them
- GRANDstack documentation: custom top level query fields
- GraphQL Architect
02:05 - Hey folks, welcome to the Neo4j Stream. 02:10 Today, we are continuing on 02:14 with our Gatsby Travel Guide, 02:18 using GraphQL and Neo4j. 02:22 Today, we're going to be adding maps using Mapbox GL JS 02:30 to our Gatsby application in two places, 02:35 which we'll talk about in a second. 02:38 Before we get started, though, 02:40 something I want to mention. 02:43 So, next week on the stream, 02:47 at the same time, next Thursday, 02:50 we're going to start something different. 02:52 We're gonna start a Fullstack GraphQL book club series. 02:59 So, if you don't know, 03:00 I've been working on a book on Fullstack GraphQL 03:06 for a while now. 03:07 And, there's a free excerpt out that available now, 03:14 which you can download here. 03:15 I'll drop the link in the chat. 03:21 And this is an excerpt of three chapters from the full book, 03:26 sponsored by Neo4j 03:28 with a focus on the back-end. 03:32 So, what we'll do, starting next week, 03:34 I thought it'd be fun 03:35 to have sort of a book club on the stream, 03:39 where we work through the exercises from each chapter. 03:44 If you wanna see the table of contents, 03:47 you can see the full one 03:48 at https://www.grandstack.io/book 03:54 that has the full table of contents 03:57 to give you an idea of what's in there. 03:58 But this excerpt anyways is what we'll start with. 04:01 It has chapters one, three and four. 04:04 So, sort of an overview of GRANDstack, 04:09 how the pieces fit together. 04:11 A little bit about Neo4j, 04:14 and the GraphQL integrations for Neo4j, 04:16 and then how we implement GraphQL back-ends. 04:21 So, anyway, so, if you wanna download that, 04:24 this is the link to the free excerpt download, 04:29 of three chapters. 04:30 And, we'll start next week on the stream, 04:32 sort of working through the exercises from chapter one. 04:38 So, if that sounds interesting, 04:41 definitely give that a download 04:44 and tune in next week 04:46 for working through chapter one together. 04:50 Buday says good morning from Australia. 04:53 Good morning to the future in Australia. 05:00 Cool. So that's for next time. 05:04 What are we gonna do today? 05:05 Well, here is the Travel Guide Gatsby project, 05:11 we've been working on. 05:12 This is the GitHub page for it. 05:15 I'll drop that link in the chat. 05:19 And, I have everything running here, 05:23 connected to my sandbox instance. 05:27 So, I've got the GraphQL API running here. 05:31 I should say the Neo4j GraphQL API running here, 05:34 that's talking to my Neo4j sandbox instance 05:36 with OpenStreetMap data. 05:39 And then, I've started my Gatsby project, 05:42 which is running on localhost8000. 05:49 I'm sorry travel guide. 05:50 So this is a statically rendered site with Gatsby. 05:57 So, it's pulled in content from our Neo4j database, 06:02 points of interest from Central Park. 06:06 We have some data here, 06:08 about each point of interest 06:10 that we're pulling in from OpenStreetMap through Neo4j. 06:13 We've stitched in images 06:15 for each point of interest using the Mapillary API 06:18 and were available, 06:19 we've pulled in data from Wikipedia, 06:24 for each point of interest. 06:27 So that's what we have so far. 06:31 What I wanna do today, 06:32 is now add some maps, 06:35 in a couple of different places, 06:37 in our Gatsby application. 06:39 The first place I wanna add a map, 06:41 is here on the index page at the top here, 06:46 cause I wanna show, 06:50 sort of Markers for every point of interest 06:53 that we have, right? 06:54 Like these are all points of interest in Central Park. 06:57 I'm gonna show those on a map. 06:58 So, you can have an idea of where they are. 07:00 And then, I want the map to be interactive, 07:02 so I wanna be able to click on a Marker. 07:06 I wanna know what point of interest it is. 07:09 Or, maybe like what category it is, 07:11 we have types for all of these. 07:16 Which I guess we're not actually showing here, are we? 07:20 Let's see. 07:23 We have a name and a type anyway. 07:26 And then, I wanna link to the actual detail page, 07:31 for the point of interest from the map view. 07:35 So that's one place we wanna add a map, 07:36 just like an overview. 07:38 But then, also, if you remember, two sessions ago, 07:43 we spent a bunch of time, 07:44 showing how to do pathfinding graph algorithms 07:50 to do efficient routing between points of interests. 07:53 And so, what I wanna do today is take advantage of that. 07:58 So, at a second map on each detail page, 08:04 for each point of interest. 08:05 That shows us on the map where the point of interest is, 08:09 but also will allow us 08:11 to select any other point of interest. 08:13 And then, draw us on the map, 08:16 the most efficient route 08:19 that we've calculated using, 08:21 and in this case, the A* PathFinding graph algorithm, 08:25 to get us to our next point of interest, 08:27 for our tour through Central Park. 08:31 So that's the plan for today. 08:35 We're going to use Mapbox to do that. 08:41 We'll need to take a look 08:42 at some react wrappers around the Mapbox GL JS library. 08:49 And then, we'll do something new 08:51 that we haven't done with Gatsby yet, 08:54 which is query r Neo4j GraphQL API, sort of dynamically. 09:03 So, outside of the build process. 09:05 So far, we've used that GraphQL API 09:09 that has data from Neo4j and OpenStreetMap. 09:13 We've used that during the Gatsby build process 09:16 to build these pages. 09:20 But that's just static content, right? 09:24 So, we've built these pages statically, 09:29 so that when we actually load these pages, 09:31 we're loading static HTML, 09:32 we're not actually hitting the GraphQL API. 09:36 But, because we want these routes to be dynamic. 09:39 One, we don't know what the user is going to select. 09:45 So, we wanna calculate those routes, 09:48 as the user is selecting those routes. 09:50 But also, you know, 09:51 maybe you wanna be able to factor in things, 09:54 like traffic conditions 09:56 or if a sidewalk is under construction, something like that. 10:00 So, we wanna be able to update our OpenStreetMap database, 10:04 on the back-end and have routes changed to reflect that. 10:07 So, we don't wanna build all these ahead of time statically. 10:10 So that, anyway, that's something new. 10:11 So, we'll have to figure out, 10:12 how we can use Apollo Client within Gatsby 10:18 to query our GraphQL API outside of that build process. 10:24 Cool. So that should be fun. 10:28 Okay, so I linked the code to get started. 10:31 I guess the other thing, 10:34 if you're following along, that you'll need, 10:36 is the specific link 10:40 to the specific sandbox. 10:42 So this is a link to the specific OpenStreetMap sandbox 10:48 that you'll need 10:50 that has all of the data from OpenStreetMap, 10:54 on our points of interest 10:56 that we use to build our routing graph on top of. 11:02 Renato says hello. 11:04 Hi, Renato. Where are you joining from today? 11:09 Okay, cool. So, let's get started. 11:10 So, first off, we wanna add a map right here, 11:16 with Markers for all of our points of interest. 11:21 How are we gonna do that? 11:22 Well, we said we're gonna use Mapbox GL JS. 11:27 So, let's take a look at that. 11:34 So, Mapbox GL JS, 11:38 is a really nice JavaScript library 11:43 for adding interactive maps into your application. 11:47 The GL comes in that it uses Web GL 11:51 to render maps from vector tiles. 11:55 So, we get nicely performance maps there. 12:00 It also hooks into Mapbox styling. 12:04 So, we can style maps, 12:06 using, I think it's called Mapbox Studio 12:08 and hook in to lots of different Mapbox APIs, 12:11 which we won't play around with too much in this case. 12:16 We will not use any Mapbox specific features really. 12:21 There's another library called Leaflet, 12:24 which I think Mapbox JS is based on. 12:27 There's some relationship there 12:28 that has very similar API. 12:30 So, we could use leaflet as well, 12:32 but I like this library. 12:34 There's nice react wrappers for it. 12:38 So, in addition to rendering the map and the tiles, 12:42 what are we going to need? 12:43 Well, we look under Markers and controls, 12:48 we'll need to be able to display Markers on the map, 12:51 so that's good. 12:52 And then, we're also gonna work with popups. 12:54 So, if we take a look at, 12:58 there's some examples here, 13:01 yeah, displaying a popup on click. 13:05 So this is kind of what we want, 13:06 is wanna add a marker 13:08 for each of our points of interests. 13:11 And, when we click on it, 13:13 we wanna show the name, the type of the point of interest, 13:16 maybe we could render our Mapillary photo in here too. 13:21 Maybe that'd be a good one. 13:22 But then, the important thing, 13:23 is we wanna link to the detail page, 13:24 for the point of interest, 13:26 so we can click into it. 13:30 Buday asks, "Will there be any date, time input, 13:34 in the Central Park data?" 13:38 I don't think so. 13:42 I don't think we have any date, time data 13:46 that we're working with here. 13:47 I can think of a few features we might want to add 13:52 that would factored date, times stuff into account. 13:57 Maybe something like showing the walking time 14:06 or the travel time between points of interests. 14:10 We know the distance of the route. 14:14 So that might be something we could calculate 14:17 if we make some assumptions 14:19 about how long it takes to walk a certain distance. 14:22 We could also maybe keep track 14:24 of like a list of, 14:25 when we visited certain points of interest. 14:28 That might be something to keep track of, 14:30 like a travel journal or something like that. 14:34 So, I can see maybe, 14:36 we get to some of those things, 14:37 but not today anyway. 14:40 I don't think we'll touch on. 14:44 Okay, so that's Mapbox. 14:46 And, you can see from the examples here, 14:48 how the Mapbox API works. 14:53 But, we're using Gatsby which uses react, 14:56 so we could sort of build our own wrapper around this. 15:04 Create our own sort of Mapbox react components. 15:09 But, there are a few things we'd have to think about, 15:12 such as managing state for the map. 15:21 React likes to control states, 15:25 we kind of have to use a ref 15:26 to just sort of pass that through. 15:29 I did this for another example. 15:31 I think this is still online. 15:37 What was this called? 15:41 Let's see if we can find it. 15:42 I think it was business reviews. 15:57 Business search, not that. 16:05 Datetime, no. 16:12 And, it was routing. 16:20 We do that in the routing app. 16:21 There's a simpler one, though. 16:28 Business search routing. 16:37 Well, in the Willow one, 16:38 if you remember the Willow real estate project 16:40 that we did on the live stream. 16:42 The previous project, 16:44 we used a react wrapper around Mapbox, 16:49 which is what we will do today. 16:57 Oh, yeah, spacetime reviews. 16:58 That's what it was called. 17:00 This one. 17:02 So, in this project, 17:04 so the idea here, 17:05 is we had some, that's too big. 17:10 We had some business reviews data from Yelp. 17:16 And, what we wanted to do, 17:17 is be able to filter in this distance radius, 17:21 move this marker around, 17:23 find businesses within that radius, 17:25 also filter by date range. 17:28 And then, do some dynamic calculation, 17:30 on the distribution of, 17:31 I think we're looking at the stars of businesses. 17:35 Or, the stars of the reviews. 17:38 And, oh yeah, the got a category distribution. 17:43 That's what it was. 17:45 But, anyway, so in this case, 17:46 this was a react application. 17:47 And, we created a map component here, 17:58 that wrapped Mapbox GL JS, 18:06 which you can see here. 18:09 So, it was a bit awkward, though, 18:11 because we had to think of keeping track 18:14 of the state for the map outside of react. 18:19 Wouldn't it be nice, 18:20 if we could sort of use react states 18:23 to also work with our map components. 18:27 And so, for that sort of usage pattern, 18:31 there are a few react wrappers around Mapbox. 18:35 We used one of them in the Willow project. 18:37 So, if you remember that, 18:39 we will use the same one again, 18:43 which is this one react-map-gl from urbica, 18:56 which is like a design studio. 18:59 So, they make really cool interactive data visualizations. 19:03 And, they've built this react wrapper around Mapbox GL 19:08 that exposes lots of different controls. 19:13 The only two that we need right now though, 19:16 are Marker and Popup. 19:19 So those are available as react components, 19:21 which is exactly what we need. 19:25 There's a similar one to this 19:27 that comes from Ubers visualization studio 19:32 that I've used. 19:33 Also, that I think now has similar features at this point 19:38 to the urbica design version. 19:44 You know, we'll use this one today. 19:45 I like this one. 19:46 We've used it before. 19:48 So, let's take a look at the docs. 19:54 See how we can add this. 19:57 So, first off, installation. 20:00 Let's go ahead and install it. 20:04 So, npm install, save, urbica, react, map-gl and mapbox-gl 20:24 And, while that's installing, 20:26 let's look at the docs here. 20:29 So, here's an example for an static map 20:34 or an interactive map, 20:37 I'm gonna start with the interactive, 20:39 oh, we get an error. 20:41 API access token is required. 20:45 So, we need a token. 20:50 Let's go to Mapbox and get that. 20:52 You can get a token for free from Mapbox, 20:59 up to so many renders of the map are free. 21:07 And, if you hit a certain threshold, 21:14 then you have to start paying for their service. 21:18 But, we can use your free token. 21:23 So, if you have a Mapbox account, 21:24 you can sign in for free. 21:26 I have lots of free map use to go, 21:29 not even approaching the paid version. 21:35 I'm just gonna use the same token 21:38 that I used for Willow. 21:42 I think that's fine. 21:45 We'll copy that. 21:47 I'm going to save that somewhere off screen for now, 21:55 we'll come back to that. 21:56 What I wanna do it now though, 21:57 is paste this in the docs. 21:59 So, we need to provide our own key in the docs, 22:03 for it to render, that's fine. 22:05 Okay, so here's an example. 22:08 So, we can see what's going on here. 22:12 We're using the useState, react hook, 22:16 for managing state in our react component. 22:19 And then, we create a viewport state variable. 22:25 So that's latitude, longitude and zoom. 22:27 So this is the center of the map. 22:29 Let me zoom in a little bit. 22:31 Speaking of zoom. 22:33 Center of the map and then the zoom level of the map, 22:37 are stored in the state variable. 22:39 And then, on ViewportChange, 22:42 is the setViewport function, 22:45 which is our react state function 22:48 for updating the viewport. 22:50 So, you can see we're keeping track 22:51 of the state of the map in the react component. 22:55 So that as we maneuver around, 22:59 that's changing the viewport latitude and longitude, 23:03 of our react component 23:05 by calling this setViewport function. 23:12 Okay, I think that's enough 23:14 to stick a map in our Gatsby applications. 23:21 So, let's go to, 23:26 what are we looking for here? 23:27 We're looking for the index page. 23:36 So, just looking at the documentation over here, 23:40 we'll need the useState hook. 23:47 And then, we'll need to import MapGL 23:54 from "urbica/react-map-gl" 24:00 And, we'll leave this CSS so that it looks pretty. 24:09 Mapbox-gl.css 24:17 Okay, and then, in our component, 24:21 we need to create a viewport state variable. 24:35 And, we'll set the initial latitude. 24:40 This is what? San Francisco? 24:45 Latitude, the longitude and the zoom level. 24:52 We don't want just go though. 24:55 Central Park latitude and longitude, there we go. 25:01 So, let's center this on Central Park. 25:06 So, latitude 40, 78, 12. 25:13 Longitude 739665, West, Northwest. 25:19 It's negative 73. 25:24 Okay. 25:28 And then, where should we put it? 25:33 Well, 25:39 We have the title and the bio component 25:46 and then we start iterating 25:48 through each one of these. 25:53 So, maybe let's put it, just before this list. 25:57 Right before we start to list all of the posts. 26:02 So that would be right here. 26:08 It's gonna bounce back. 26:11 So, MapGL. 26:19 I'll just do the self-closing there. 26:22 And then for the props, style. 26:25 Style will be simple, 26:26 we'll just let it take 100% width and height. 26:40 And, mapStyle. 26:43 So this is where we could make, 26:45 this our own custom style using Mapbox Studio. 26:50 Let's just use this default style for now. 26:54 Light v9. 27:01 Okay, our access token. 27:04 Let's come back to this one. 27:07 We don't wanna just paste in our token there 27:12 and have that checked into our code. 27:13 We wanna put that in as an environment variable. 27:16 So, our latitude, 27:17 this is gonna be viewport.latitude. 27:20 So, as we're moving the map around, 27:25 we want our viewport state variable 27:28 and our react component to update. 27:30 So, we're gonna bind that to the prop latitude here. 27:34 Same for longitude, 27:40 viewport.longitude and I spelled longitude wrong. 27:46 Longitude, not tied. 27:51 Zoom, same thing. 27:53 Bind that to viewport.zoom. 27:58 And, on ViewportChange, 28:03 we want to call setViewport, 28:07 which is our function 28:10 that's gonna update our viewport state variable. 28:14 Okay, so the only thing we're missing here, 28:17 is the access token. 28:19 So, we said we wanna set this as an environment variable. 28:23 How do we do that in Gatsby? 28:27 Let's take a look. 28:29 Gatsby environment variables. 28:36 So, there are a couple of ways 28:39 to think about and using environment variables in Gatsby. 28:42 One; is build time variables, right? 28:48 So, Gatsby runs a Node.js process at build time. 28:59 And, we may use some variables there, 29:01 such as, I don't know, 29:03 connecting to some other GraphQL API, 29:05 stitching it in like what we do 29:07 with our Neo4j GraphQL API. 29:09 And then, there are the client side environment variables, 29:15 like here for our access token, 29:18 which aren't really defined, 29:20 as operating system environment variables, right? 29:24 These are just sort of substituted in the static HTML. 29:40 So, what we're gonna do, 29:42 is according to this, 29:45 if we create a .env.development 29:49 and .env.production, 29:51 then Gatsby build process, 29:56 will substitute in environment variables that we specify, 30:01 prefixed with GATSBY. 30:03 So, in this, this sort of name, space, GATSBY underscore. 30:08 So this will be right here, 30:13 process.e, oops, not processing instructions. 30:19 process.env.GATSBY_MAPBOX_KEY, 30:27 let's call it. 30:30 And then, we need to create a new file up here, 30:37 called .env.development. 30:43 Oh, which we already did. 30:47 And, paste in our Gatsby MAPBOX KEY. 30:54 Cool. 30:58 So, let's see what that looks like. 31:06 All right, so, let's do a gatsby develop 31:10 to build our site again. 31:21 And this will take a few seconds 31:23 because we need to check the Mapillary API 31:31 for each point of interests. 31:33 I think that's the, at this point the slowest part 31:38 of our build process. 32:03 Did anyone attend NODES online conference this week? 32:11 I know Buday did. 32:13 Gave a great talk about security using GraphQL apps, 32:24 which was really cool. 32:25 Was there anyone else at NODES? 32:28 What did you think? 32:29 Any feedback? Any thoughts? 32:36 If you don't know what I'm talking about, 32:38 that would be the NODES 2020 online conference, 32:47 which was a couple days ago. 32:51 And, one thing that's neat, 32:54 is all of the videos and slides are up now. 33:06 So, if you just wanna watch the recordings, 33:10 check those out, I'll drop a link in the chat. 33:11 There was a keynote from Emil. 33:15 And, lots of talks about Graph Data Science. 33:21 Some talks about GraphQL, Visualization. 33:25 A look at building applications with Neo4j. 33:28 So, lots, lots of good stuff there. 33:30 Hopefully. Hopefully, it was enjoyable, if you made it. 33:36 Not to see was there. Great. 33:44 Okay, cool. Our site has built. 33:50 Buday says, "The graph-native machine learning, 33:52 announcement was great." 33:53 Yeah. There's a lot of interesting stuff going on, 33:55 in Graph Data Science. 33:59 That's a really interesting area. 34:01 I think the graph embeddings stuff was really interesting, 34:06 which was talked about. 34:07 Yeah, definitely check out those recordings 34:10 if you missed it. 34:12 Okay, our site has built but I don't see a map. 34:17 Let's see what happened there. 34:20 Let's open up our console. 34:26 What happened? I see a warning. 34:35 But, I don't see our map. Why not? 34:42 Let's inspect. 34:49 So, 34:54 there is a map here. 35:02 But, why don't we see it? 35:05 It doesn't have any height. 35:08 So, let's just specify a height here in the style. 35:17 600 px, there we go. 35:23 Okay, there's the map, 35:24 here's Central Park. 35:25 We could probably zoom in a bit more initially, 35:29 maybe like a 13. 35:32 It's everything, there we go. 35:34 All of our points of interest are going to be here, 35:38 in Central Park. 35:42 Cool. Means we are moving around, 35:44 and zoom in. 35:48 Cool. So now, we wanna do, 35:51 is add some Markers for our points of interests. 35:57 So, let's look at the docs 35:59 and see how we do that. 36:01 We want Marker. 36:05 And, a Marker, 36:08 is just a child component of the map. 36:13 So, let's import Marker 36:25 and create a new Marker. 36:39 Oops. 36:44 As a child component, 36:51 it needs to have some content in the Marker, 36:58 which will rebound in seconds. 37:01 Okay, and it needs latitude and longitude. 37:11 And then, this is a draggable Marker. 37:19 Let's add in our key here, 37:24 so we can see what this looks like. 37:27 All right here. So, here's a big giant Marker. 37:37 And this is draggable. 37:38 But, we don't wanna be draggable. 37:40 Our points of interest are not gonna move. 37:42 So, let's not worry about that. 37:45 And, let's just use viewport latitude 37:54 and viewport longitude. 38:03 So, we'll just put a marker in the center of the map. 38:08 And, there it is. 38:09 So, notice that our marker, 38:11 we don't have, like an icon or something by default. 38:16 It can be whatever we want to stick in there. 38:23 Including, so, when I did this previously, 38:25 on the real estate application, 38:28 we had a div that we added some CSS styles 38:32 to make it like a blue dot or something. 38:35 But, we can also use svg. 38:39 So, I cheated and found svg 38:41 of a small like pin marker. 38:46 So, I'm just gonna copy paste that in. 38:59 And now, so it's a little tiny red pin. 39:06 Okay, so that's fine, 39:08 but we don't want a marker, 39:11 just in the center of the map. 39:13 We want it for all of our points of interest. 39:17 How are we going to do that? 39:20 Well, if we look at what's going on in this page. 39:24 If we remember what this page is, 39:26 so this is the index page, 39:29 and there's a QraphGL query here 39:32 that is fetching all of the posts. 39:39 So, we have what we need already. 39:46 And, we can see here, 39:47 where we're mapping over this post variable 39:50 that has the detailed information 39:53 for the post including latitude, longitude, 39:57 name and type. 40:00 So, what we wanna do, 40:02 is render a Marker for each post. 40:08 So, we'll say posts.map 40:15 and then iterate over each point of interest. 40:19 We'll also get the index. 40:21 We'll see why that is needed in a second. 40:25 And then, for each point of interest, 40:30 we're gonna return a Marker component. 40:34 And, we'll set the key prop to be the index 40:38 and the post array to react once, a unique ID, 40:42 anytime we created an array of these components. 40:49 And, the longitude, 40:50 is going to be poi.location.longitude, 40:58 and latitude is poi.location.latitude. 41:10 That's the Marker. 41:11 And then, in each Marker we have, copy again, our svg. 41:31 And, let's delete our previous Marker 41:36 that we were just putting in the middle of the map. 41:38 Since I don't really need that. 41:46 And now, we've got Markers 41:52 for all of our points of interest. 41:57 Cool. 42:00 So that's a start. 42:05 Next thing we need, 42:06 is when we click on one of these, 42:08 we want to show a popup. 42:12 And that popup should have detailed information 42:15 about the point of interest 42:17 and also link us to the detail page 42:22 for each point of interest. 42:25 So, if we take a look at the docs here for Popup, 42:31 and let me find my Mapbox key again, 42:36 so you can see what this looks like in the docs, here. 42:46 Okay, a Popup with some content into it. 42:49 Yep, that's what we want. 42:53 So, how do we sort of bind a Popup to a Marker? 42:59 Do we need to create a Popup 43:04 for, oops, I guess we are not doing it. 43:06 Maybe create a Popup for every single Marker? 43:08 Sort of like as we do this iteration? 43:14 And then, show and hide it when we click on the Marker? 43:19 Well, we don't need to do that. 43:20 We only need to create one Popup component 43:23 because we're only gonna be showing, 43:25 one Popup at a time. 43:26 So, you can only select one Marker, 43:30 one point of interest at a time. 43:33 So, we're only gonna need one Popup. 43:38 How are we then going to figure out, 43:41 what to display in the Popup? 43:44 So, what we'll do, 43:46 is we will use another state variable. 43:54 Let's go ahead and create that. 44:04 Let's call this showPopup and setShowPopup 44:18 and initially set this to false. 44:20 So, we're gonna have this state variable. 44:22 And this showPopup state variable, 44:25 is gonna keep track 44:27 of if we should show a Popup or not. 44:31 And, also, you need to keep track of the information 44:34 that we need to show in a Popup 44:35 but let's worry about that in a second. 44:40 So, right here after we've rendered all of our posts. 44:53 We'll say, if showPopup is true, 45:02 then render the Popup. 45:13 Let's look at your docs for the Popup here. 45:20 Did we import it up here? 45:23 No. Import Popup. 45:29 Marker, Popup. 45:36 Okay, and our Popup needs some location, right? 45:47 So, needs a latitude. 45:58 For now let's just use viewport.latitude. 46:00 So, it'll just show up in the middle. 46:03 And, the longitude, viewport.longitude. 46:12 closeOnClick. 46:17 So, do we want pop the close when we click it? 46:20 No, I don't think so. 46:21 I'm sure the default is there. 46:25 What else? We can also set a tipSize. 46:28 We're looking all the props 46:31 that we pass. 46:34 I think there's a tipSize. 46:35 Except that it's a five. 46:40 Will anchor it at the top. 46:48 And, 47:00 well, let's set this to true. 47:05 So, if we save this right now, 47:08 are we going to see a Popup? 47:12 I think that we will not see a Popup. 47:15 Nothing happens when you click a Marker. 47:17 Why don't we see a Popup? 47:18 Well, because we set showPopup initially to false. 47:24 And, here, we're checking, 47:27 its showPop truthy. 47:31 If it is, then render the Popup. 47:35 If we change this now to true. 47:40 So, set the initial showPopup value to true, 47:45 then yeah, here's our Popup, 47:47 and it didn't give any content to our Popup. 47:51 So, it's kinda hard to see. 47:52 Let's add something in there. 47:57 We can put a div in there. 48:00 That's a sign of a Hello there. 48:06 Okay, there's our Popup. 48:07 We can close it by clicking that x, 48:09 but then we don't get it back. 48:11 So, okay, we want to show the Popup. 48:17 So, what we're gonna do is on our Marker, 48:27 we are going to add a click handler. 48:36 So, let's add that. 48:40 I think we can add that here. 48:48 So, onClick, 48:54 we're going to call set. 48:58 What do we call it? setShowPopup. 49:01 Set that to true. 49:04 Then we format. 49:06 There we go. It's a bit more readable. 49:08 So, when we click on our Marker, 49:10 we're gonna call this setShowPopup function 49:19 that is going to update our showPopup state variable. 49:30 And, let's change now the initial value to false. 49:39 So, initially don't show the Popup. 49:41 But, when I click one of these Markers, 49:44 then you should show it. 49:51 Okay, what's going on there? 49:52 So, I'm clicking, now I'm clicking but I'm not seeing it. 49:55 Well, that's because our ShowPopup, 50:01 in the visibility of the Popup got out of sync. 50:05 So, we need to explicitly for our Popup, 50:11 we need to explicitly call. 50:14 We look at the docs here. 50:18 The onClose function. 50:23 So, onClose when our Popup is closed, 50:33 we need to call setShowPopup 50:39 and set that equal to null or false. 50:44 So now, I click on Marker, let's refresh. 50:53 I click on Marker, it shows me the Popup. 50:57 That is setting the showPopup state variable to true, 51:01 I close it. 51:02 That now, the onClose handler, 51:08 call setShowPopup, sets it as null. 51:11 And, Popup goes away. 51:14 Click on one of these again 51:16 and I see my Popup. 51:19 Okay, cool. 51:21 But, I want the Popup to do more than just say Hello there. 51:26 I want it to tell me what point of interest I clicked on. 51:29 I want it to link to the detail page 51:32 for the point of interest. 51:34 How do we do that? 51:35 Well, up here, when we click on a Marker, 51:40 and we call setShowPopup. 51:44 Instead of setting it to true, 51:46 let's pass in the poi. 51:50 So, pass in the point of interest object 51:53 that we're iterating over. 51:56 So now, instead of true or false, 52:01 showPopup is going to be an object 52:03 that has our point of interest information. 52:10 So, our latitude will be showPopup.location.latitude. 52:23 Longitude will be showPopup.location.longitude. 52:32 And, we can start to show the details now in the dev 52:41 in the content for the Popups. 52:42 So, what should this be? 52:46 How about string, so in bold, showPopup.name. 52:59 And then, I think we have the type as well. 53:02 Let's put that in parentheses, 53:11 showPopup.type. 53:15 And then, we can link to it. 53:22 Target, open a new tab. 53:27 When we click on, it's supposed to be a link 53:28 to the detailed page, 53:31 which is going to be, 53:35 what is this gonna be? 53:36 This is gonna be /showPopup.node_osm_id 53:47 So, we're using the id of the point of interest 54:04 and call that Details. 54:10 And, what did I do wrong there? 54:15 Href, 54:22 closing curly brace. There we go. 54:28 Okay, we don't need this closing curly brace. 54:35 Okay, so that'll link to the detail page 54:41 for the point of interest, 54:43 if we rebuild that. 54:45 And, let's refresh over here. 54:52 Cool. So now, when I click on one of these, 54:54 that says Bank Rock Bay (water), 54:58 I have it linked to the details page. 55:01 And, I linked to the detail page for that point of interest. 55:08 Tennis Court. 55:11 What else do we have? 55:13 Bicycle parking. 55:15 Here's the reservoir. 55:20 Cool. 55:23 What else? 55:24 Oh, we said we could add a photo. 55:29 So, let's see, do we pull that in, 55:32 we don't pull in the photos. 55:36 So, let's add that to our GraphQL query for rendering this. 55:42 So, photos. 55:47 Let's just grab the first photo. 55:50 So now, we'll have photos in this poi object. 55:57 So, we can render an image as well. 56:05 So, the image, 56:08 let's set this to be kind of small. 56:13 And, Source is gonna be showPopup, photos. 56:21 And, that's an array. 56:22 So, you grab the first photo, URL. 56:28 And, let's see if that worked. 56:33 There's a point of interest. 56:34 Yeah, cool. So now, we've got, 56:37 if we have them, 56:38 we don't have these for all of our points of interest. 56:41 But, if we find a photo from the Mapillary API, 56:46 will now show it in the preview. 56:54 Cool. 56:57 And, we have a link to the detail page. Great. 56:59 So, I think that is what we wanted for the index page. 57:06 So, we're showing on the index page Markers, 57:10 where all of these points of interest are. 57:11 We saw the list. 57:13 So, we can get to our detailed page that way. 57:20 But, we can at least see the map view now, 57:23 where these things are. 57:26 Okay, so that is the first part, 57:30 where we said we wanted to show a map. 57:35 The second place we wanna map is on the detail page. 57:40 We wanna show a Marker where the point of interest is. 57:49 We want to have a drop-down then, 57:51 that's gonna show all of the other points of interest, 57:56 and then show you a route 57:59 from one point of interest to the other. 58:02 So, let's first look at our map 58:08 and see what components we can use to do this. 58:14 If we look at the Source component. 58:18 And, let's grab our Mapbox key again. 58:26 And, plug that in here. 58:38 Here, we're using a Source component to render GeoJSON. 58:44 This is a polygon. 58:46 We don't really want a polygon. 58:50 Here's a GeoJSON line, LineString. 58:53 That seems like more, like what we want. 58:59 Yeah, here we go. 59:00 So, something like this. 59:01 So imagine, we're showing the route from here to here. 59:07 We know from two times ago, 59:12 we can get something like this, 59:15 calling our GraphQL API. 59:17 So, specifically, the Neo4j GraphQL API, 59:20 that is using those pathfinding functions, 59:24 specifically A* to find the route 59:27 from one point of interest to the other. 59:29 So that's what we wanna render on the map. 59:30 So, we're gonna use this Source components. 59:33 We take a look at that. 59:34 How does this work? Well, Child components for the map, 59:40 we have a Source that is a geojson, 59:45 of type geojson 59:47 and it takes a data prop. 59:49 So, the data prop is the geojson, 59:53 so that is a Feature, LineString and then coordinates, okay? 59:57 I think we can fetch that from GraphQL API. 60:03 And then, we need to create a Layer 60:06 and the Layer specifies the type and the styling. 60:14 How's that is going to render the Source, 60:17 which is geojson onto the map. 60:20 Okay, that seems like what we wanna do. 60:23 So that sounds good. 60:28 What we want to do now, 60:30 is go to our blog-post template page 60:40 So this is the template that is used 60:43 for rendering each of these detail pages. 60:53 So, the first thing we wanna do, 60:56 is import our map in here. 61:03 So, let's do that. 61:06 So, looking at the docs, again. 61:13 Need useState hook 61:22 and our Map and Source and Layer, 61:31 components and our css. 61:49 And then, in our component we need viewport. 62:06 And, here, we're setting our latitude. 62:14 Let's use the default that we have here initially, 62:20 that's fine. 62:23 Well, actually, since we're gonna do routing, 62:26 why don't we use the same latitude and longitude 62:29 that we used for setting the view 62:41 to be centered on Central Park. 62:43 So, I'll just copy that from last time, 62:45 and that way the map will be centered in the same spot, 62:50 as our main map, 62:52 which should look good. 62:56 Okay, and then we need to render our map somewhere. 63:02 If we look at the detail page, 63:04 maybe we should render the Map right below the image. 63:09 It seems like a good spot. 63:17 So, we'll put our Map here. 63:25 style is, let's just copy this from here, 63:36 so we don't have to type this again. 63:41 Most of this is gonna be the same. 63:51 Let's format that, there we go. 63:59 Okay, so, style, Map style or accessToken, 64:05 latitude and longitude and zoom. 64:11 And, we get some warnings and an error, 64:17 specifically, spelled MapGl wrong, MapGL. 64:22 The L is capitalized. 64:24 Okay, cool. 64:25 Here's our map. Great. 64:29 Okay, cool. So now, what we want is a Marker 64:37 for wherever we are, 64:38 wherever our current point of interest is. 64:44 So, let's add that. 64:47 Did we import Marker? 64:50 Source, Layer and Marker. 64:53 Market, marker, there we go. 65:00 Okay, so we've got our Marker, 65:02 and longitude is going to be what? 65:09 Well, remember, we have this post object. 65:12 So, if we look up here, 65:16 post equals data.poi.pointOfInterest, the zero index. 65:23 What's going on here? 65:24 Well, for each of these template pages, 65:28 Gatsby runs a GraphQL query, 65:30 against the Gatsby GraphQL API, 65:32 which is down here. 65:35 And, so this means, 65:37 that for each one of these pages, 65:43 this is the information that we have. 65:44 So, we have all of these fields here in the selection set. 65:52 So, we have latitude and longitude, photos, and so on. 65:55 So, we have latitude and longitude, 66:01 which is going to be post.location.longitude. 66:13 And, latitude is post.location.latitude. 66:27 Okey-doke. 66:30 And, I'm gonna use my same svg 66:32 that I copied from another Map example. 66:37 So, let's drop that in here. 66:49 Okay, and so, because we want to use this 66:59 as kind of like a stop starting point. 67:03 So, here we can see our Marker. 67:07 Let's, I guess first of all, 67:09 let's make that a little bit bigger. 67:16 Let's get rid of this transform that, don't need that. 67:21 And, let's change the color to green. 67:29 So, green is our starting point. 67:33 So, here we are for the reservoir. 67:37 This is our starting point. 67:40 If we go back and select some other, 67:44 so maybe, I don't know down here. 67:45 It's this one, the Musical Clock. 67:48 We go to the Musical Clock page. 67:50 And, see yep, we're down here. 67:53 The Musical Clock. 67:56 Cool. 67:59 So, a couple questions in the chats. 68:02 "Can we face n+1 problems using Neo4j?" 68:06 Do you mean, the n+1 problem with GraphQL? 68:11 So, in the context of GraphQL, 68:14 when we build the back-end for a GraphQL API, 68:18 so let's look at this selection set, for example. 68:21 So, because GraphQL has these very nested queries, 68:26 oftentimes, we'll make a query for, in this case, 68:31 say the point of interest. 68:33 And, we'll go to the database 68:34 to find the point of interest, 68:36 by, in this case, node_osm_id. 68:40 And then, we say, 68:41 "Oh, well, now we need some information 68:43 about the location of that, 68:46 the tags for it. 68:47 And, we might make multiple round trips to the database 68:52 to fetch that information." 68:54 So, in GraphQL, this is the n+1 query problem. 68:58 And, because we don't wanna do that, 69:00 we want to only make one round trip to our data layer, 69:04 because that's going to be a lot faster. 69:06 Making multiple round trips 69:08 when resolving a GraphQL request, 69:10 introduces a lot of latency. 69:14 So, we don't want that. 69:15 So, the question is, 69:17 when we use GraphQL with Neo4j, 69:21 do we have this n+1 query problem? 69:25 And, the answer is, 69:26 not using the neo4j-graphql.js library that we're using. 69:32 So, if we take a look at the code 69:37 for our neo4j graphql API, 69:39 you can see we're using this neo4j-graphql.js library, 69:44 where we just pass in our type definitions 69:48 to makeAugmentedSchema. 69:50 And that generates an executable schema object. 69:53 The only resolver we wrote, 69:55 was the one that's calling out to the Mapillary API. 69:58 So, what that library is doing, 69:59 is for any arbitrary Graph QL request. 70:03 So, even this one. 70:04 It's generating a single database query. 70:07 So, if we look, here. 70:10 We don't have our logging enabled 70:12 to show the generated queries. 70:14 But, what's going on there, 70:17 is we're generating a single database query 70:20 from any arbitrary GraphQL request, 70:23 so they were only sending one database query, 70:26 and then letting Neo4j optimize the execution plan 70:32 for that query. 70:33 And, because graph databases like Neo4j, 70:35 are optimized for these traversals, 70:38 which is like this nesting in a GraphQL selection set. 70:42 It ends up being really fast. 70:45 There's some more information in the docs. 70:53 I think in this schema design. 70:58 Yeah. So this page talks about it a little bit. 71:09 This n+1 query problem. 71:11 And then, so, if we look at here, 71:15 single database query, one of the goals. 71:20 And then, I also wrote a blog post called, 71:23 let's see if I can find it, 71:24 Five Problems with GraphQL 71:30 and how Neo4j Solves Them, something like that. 71:33 Five Common GraphQL Problems, 71:35 and How Neo4j GraphQL Aims To Solve Them. 71:39 Yeah, and so in this blog post, 71:42 talks, specifically about this n+1 query problem, 71:47 how we address that with Neo4j GraphQL. 71:51 So, to answer your question. 71:53 No, we did not have the n+1 query problem 71:57 when we're using GraphQL with Neo4j, 71:59 if we're using one of the GraphQL integrations for Neo4j. 72:03 Because, we're generating a single database query, 72:06 which then gets optimized better than the base. 72:10 So, great question. 72:14 The legendary Dosa Man, the honor Marker. 72:18 I don't know. Who's the Dosa Man? 72:21 Let's, 72:27 Oh, is this like a food truck? 72:31 Is he in Central Park? 72:34 I'm not sure if, 72:38 not sure if we have that from OpenStreetMap. 72:43 That is a good question. 72:47 If he's listed in OpenStreetMap, 72:51 then we should have him in here somewhere. 72:56 That's a good point. 72:57 We don't have a great way of filtering 72:59 by categories in search for Dosa. 73:02 Don't have any Dosa. 73:04 One thing we wanna think about, 73:05 is we don't have a great way 73:07 to search by category either. 73:10 So, I'm just kind of showing you a list 73:12 and we kind of have to click around 73:15 to see what we want. 73:16 But, really, we should have some way to filter 73:19 by category of the thing that we're looking for. 73:21 So that might be another feature 73:23 that we wanna think about building. 73:26 But, yeah, good point. That's fun. 73:30 And then, another question, 73:32 "How could we avoid a path inside water?" 73:35 Yeah, that is a good question. 73:38 So, if we're going from here, the Bertel Statue, 73:47 to here, Central Park Police Precinct. 73:52 Fastest way is just to cut across the water. 73:54 But, obviously, that's not reasonable. 73:57 And, what we'll see when we add our routes here, 74:00 is that that's taken care of us, 74:02 because of the OpenStreetMap data 74:04 that we've imported into Neo4j. 74:06 So, we have all of these footpaths. 74:10 And, there's a couple streets in Central Park as well. 74:12 But, we have all of those in the database. 74:17 And, our routing algorithm will take that into account. 74:23 So, what we're doing with our routing algorithms, 74:26 which we can talk more about in a sec here. 74:32 They're looking at all of these paths 74:35 that we've imported into the database 74:37 to try to find the shortest path 74:39 from one point to another. 74:42 And, hopefully, none of our paths cross over water, 74:47 so that shouldn't be a problem. 74:52 Okey-doke. 74:57 So, let's go to our detail page. 75:04 We've got our Marker. 75:08 It's in the right spot. 75:09 That's where the reservoir is. 75:15 And, what we wanna do now, 75:19 is present the options for routing. 75:24 So, we want, we will keep the UI elements simple here, 75:30 and I'll just have a drop-down, 75:35 with all the possible points of interest 75:38 that we can go to. 75:41 And, let the user select one, 75:42 and then we'll show them the routes to get there. 75:49 Okay, so, let's add that UI element. 75:59 So, maybe we'll put that right before our map. 76:10 We'll say Route to, 76:14 and then a select element. 76:21 And, we need some onChange handler for that. 76:25 Let's call that onRouteChange, 76:27 or how about onRouteSelected? 76:33 I will define that in a second. 76:37 Okay, and so, we're gonna have like options in here 76:41 that have the name of the route 76:44 of the other points of interest. 76:46 But, where are we going to get those? 76:51 Well, if we take a look at our GraphQL query. 76:59 So this GraphQL query runs 77:03 when this page is being built. 77:07 And, currently, it looks up PointOfInterest 77:14 by slug or by id. 77:17 So, it takes this part of the URL path. 77:21 This is the id, the node_osm_id, 77:25 and looks that up using GraphQL, 77:27 to get the details of the page to render. 77:31 So, what we can do, 77:32 is modify this query 77:35 to return all of the PointsOfInterest. 77:38 So, let's alias this to allPOIs. 77:43 It's going to be selecting the poi field. 77:50 And then, the PointOfInterest field. 77:56 And, previously, we passed in the node_osm_id down here, 78:02 to only give us the one that we're interested in. 78:04 But, we wanna know, 78:05 what are all of the possible PointsOfInterest 78:07 that you might be able to route to. 78:10 And, we'll need the name and the node_osm_id. 78:17 Okay, so now, 78:20 that means that on our data object, 78:25 back up here, right here. 78:28 So this data object, 78:31 so Gatsby runs this GraphQL query for us, 78:34 and then passes the results 78:36 of that GraphQL query at build time, 78:39 into this data object. 78:43 So that means, 78:45 when we add this select elements, 78:53 we can map over data.allPOIs.PointOfIterest, 79:05 map over those, 79:07 the PointOfInterest and the index. 79:13 And, we can render an option element, 79:19 where the key is the index, 79:20 Remember, with react, 79:21 we always wanna specify a unique key, 79:24 whenever we generate an array of elements. 79:29 The value, this is gonna be the node_osm_id 79:33 of the PointOfInterest we've selected. 79:35 The one we wanna route to. 79:38 And, will display the name. 79:44 Okay, so let's try that. 79:47 So that's going to rebuild all of our pages now, 79:50 because we've changed this GraphQL query 79:55 that we're running. 80:01 While we're waiting for that, let's define a Function. 80:04 So, we said onRouteSelected, 80:07 is gonna be the change handler for the. 80:11 So, let's define that up here. 80:18 So, onRouteSelected, 80:23 and that's gonna be passed in a event. 80:30 And, what do we wanna do here? 80:36 Well, this means that, okay. 80:37 The user has selected a route 80:40 or selected a point of interest. 80:41 And, we now want to go back to our GraphQL API. 80:45 And, oh, yeah, it's complaining 80:49 that we haven't defined onRouteSelected, but we did. 80:51 We did just define it. 80:53 There we go. 80:56 Okay, so, we were saying, 80:58 what do we wanna do in the onChange handler 81:01 for onRouteSelected? 81:04 Well, what we want to do, 81:05 is figure out what the route is. 81:09 And, to do that, 81:11 we're going to query the neo4j graphql API. 81:17 So, remember, we spent some time previously, 81:21 exposing the A* pathfinding algorithm through GraphQL. 81:29 So, I find the optimal route 81:34 from the current point of interest 81:37 to whichever one was selected. 81:45 How are we gonna do that? 81:46 Well, to do this, 81:48 we're gonna need to run a GraphQL query, 81:53 and then transform the results, 81:56 into that GeoJSON formats 81:59 that we saw in the docs here. 82:03 So, transform the results of that route query 82:08 into this GeoJSON format, 82:11 and add a Source component to our map to render that. 82:19 So, cool, okay. 82:22 Before we do that, let's make sure, 82:24 here's our drop-down now. 82:26 So, for each one of these, go, 82:28 let's go select a different page. 82:30 Let's select, no that's a no name monument. 82:37 There's the Azalea Pond. 82:40 Yeah, cool. So, we've got a whole bunch 82:43 of points of interest that we can route to. 82:51 Okay, cool. 82:53 Before we get into that, 82:55 question in the chat regarding GraphQL documentation. 83:03 "Is it possible to customize the GraphQL docs in schema? 83:07 The generated query hyperlink points to GRANDstack, 83:12 and the docs. 83:13 I want that to be pointing 83:14 to the documentation link I create." 83:17 Yeah, so, when you're defining, 83:21 your GraphQL type definitions. 83:27 So, like here, 83:32 for the types and the fields, 83:35 if you add doc strings to this, 83:45 then that will show up in playground, for example. 83:57 It'll show up in here. 84:00 I think we also now generate some documentation strings 84:11 that point to the documentation on GRANDstack 84:14 for things like, if you're using, 84:17 like the native date, time type. 84:20 Or, some of the generated query fields and so on. 84:24 I think I'm using an older version 84:26 that doesn't have that feature. 84:27 But, yeah, it should. 84:28 If you add your own, 84:30 it should override whatever the default is Buday. 84:36 So, if you're not seeing that behavior, 84:38 and you're not able to override the default, 84:42 in your typedefs, 84:43 then that's a bug. 84:46 I guess though, 84:48 for some of the generated things are not exposed. 84:57 Yeah. Oh, yeah. And so, like for links and stuff, 84:59 you can use Mark down in here, 85:00 I think is what we do. 85:04 But, there are some generated things I think 85:05 that aren't really exposed, 85:08 like, how would you override the documentation 85:12 for some of the native database types 85:14 that are added by the GraphQL integration to the schema? 85:17 I'm not sure if there's a way to override those. 85:22 I guess you could declare those types, 85:25 in the schema explicitly. 85:27 But, yeah, if you find you're not able to override those, 85:30 that's a bug and open an issue, 85:33 and we'll take a look at that. 85:42 Okey-doke. 85:47 So, we want to now run a GraphQL query, 85:54 when the user has made a selection in this drop-down. 85:59 So that's our next step. Right? 86:01 So, blog-post template, right? 86:06 So, filling out here, basically, right? 86:08 So, they've selected a PointOfInterest. 86:13 It's in this event object, 86:16 passed into this onRouteSelected function. 86:20 So, how do I run a GraphQl query now? 86:25 Well, to do that, we will use Apollo Clients. 86:33 So, Apollo Client, 86:37 these are the docs, 86:38 queries under Fetching. 86:43 We're using React, 86:44 so we can use the useQuery hook. 86:47 In this case, we actually don't want the useQuery hook. 86:53 And, the reason is that the useQuery hook, 87:00 will execute a GraphQL query immediately 87:05 when the component is mounted. 87:08 And that's not quite what we want here. 87:13 We only want to run this query to find the route 87:17 when a user has selected something from the drop-down. 87:24 So, in that case, 87:25 we want here, executing queries manually. 87:32 So, instead of useQuery, 87:35 we're going to use the useLazyQuery hook. 87:40 So this is kind of like, 87:42 this is more similar to I guess, the mutation hook API, 87:47 where we get a function then that we can call. 87:51 So, here, we define GET DOG PHOTO query, 87:59 pass that to theuseLazyQuery hook, 88:02 which then returns this tuple. 88:04 And, the first element of that tuple is getDog, 88:08 which is a function that we can call 88:11 to execute that GraphQL query 88:13 and pass in variables and stuff. 88:15 In their example it's happening, 88:18 near the event handler for a button, 88:20 which is basically what we're doing, 88:22 except we're doing it in the event handler 88:25 for our select element. 88:31 Okay, cool. So, you know, we wanna use Apollo Client, 88:35 we know we wanna use the useLazyQuery. 88:40 "How do we use Apollo Clients with Gatsby?" 88:44 So, Gatsby does some GraphQL things already. 88:48 I mean, we've seen how, for example, 88:51 in each of these page templates, 88:55 Gatsby runs a GraphQL query for us at build time. 88:58 We saw how that happens, 89:00 in this Gatsby node file previously, 89:06 how we use that to find all of the pages to render again, 89:11 at build time. 89:13 "But, how do we run GraphQL queries 89:17 not as like static queries, 89:20 but during outside of build time 89:27 after we've already built our application?" 89:32 Well, turns out there's a plugin for that, 89:37 called, I think it's, 89:38 is it called gatsby apollo plugin, I think. 89:46 Yes, gatsby-plugin-apollo. 89:54 And so, what this does, 89:56 is we add this plugin to the Gatsby config, 90:00 point it at a GraphQL endpoint. 90:03 We can also pass in credentials, 90:08 if we need those. 90:09 In our case, we don't. 90:11 And then, this will make sure 90:13 that the Apollo Client instance is instantiated 90:17 and injected into the React component hierarchy, 90:21 so that we can then use those Apollo Client hooks. 90:27 So, let's install this. 90:33 So, npm install, Gatsby, plugin apollo 90:43 and apollo client. 90:48 And then, in Gatsby config, 90:54 for our plugins, 91:01 we need a new plugin. 91:04 So, resolve, gatsby-plugin-apollo, options. 91:17 uri is going to be http://localhost:3003. 91:24 We'll just hard code that for now, that's fine. 91:29 And then, Gatsby developed to restart our Gatsby app. 91:42 Okay, cool. 91:43 So now, we will have a Apollo Client instance injected, 91:53 into the component hierarchy. 91:58 And, we can make use of this useLazyQuery, 92:03 Apollo Client hook. 92:07 Cool. So, while that is building, 92:12 let's take a look at the query 92:16 that we wanna run, 92:25 to do our routing. 92:28 So, on running on localhost 3003, 92:31 this is our Neo4j GraphQL API. 92:34 Let's jump back and take a look at the code for that. 92:44 So, here's the type depths for that GraphQL API. 92:49 That's maybe a bit too big, I guess. 92:52 We have Steps. 92:56 This is basically is the point type 92:57 that has latitude and longitude. 92:58 This is meant to represent each step in a path. 93:02 We have tags. 93:03 So these are the OpenStreetMap tags. 93:05 And then, we have PointsOfInterest. 93:07 And, on each PointOfInterest, 93:11 we have a routeToPOI field 93:19 that takes a poi, 93:22 so an ID of some PointOfInterest. 93:25 And, it returns a array of Steps. 93:29 So, a bunch of latitude and longitudes. 93:33 Okay, so that means, 93:34 that we've resolved some PointOfInterest. 93:39 We wanna route to another one. 93:41 And then, the logic for that, 93:44 we're using a cypher schema directive. 93:47 So, cypher schema directive, 93:50 this is my favorite. 93:57 We've got this one. 93:58 This is my favorites. 94:00 A bit of functionality in the Neo4j GraphQL integrations, 94:07 that allows us to define custom logic in cypher 94:12 and attach that to a GraphQL fields. 94:18 And, in this case, 94:19 we are using the Graph Data Science Library, 94:25 using the A* algorithm 94:27 to find the shortest path 94:31 between these PointsOfInterest. 94:32 So, let's run this. 94:33 So, let's say, 94:36 let's get the first 10 PointsOfInterest. 94:44 Turn the name and nose_osm_id, 94:49 and then we want to routeToPOI. 94:55 So, let's find the route from the reservoir to the pond. 95:04 That's gonna give us latitude, longitude. 95:09 We'll change this from first 10 PointsOfInterest 95:13 to just the first one. 95:20 So, we're saying, 95:21 find the first PointOfInterest, 95:22 which in this case happens to be the reservoir. 95:27 Give me the name and the node_osm_id 95:30 of that PointOfInterest. 95:31 And now, find the route to this other PointOfInterest. 95:36 And, I get a whole bunch of longitude and latitude pairs. 95:41 Okay, cool. So this is the query 95:43 that we wanna run 95:45 when a user selects that drop-down. 95:48 So, we want to run this query, 95:53 but basically replacing the poi 95:57 and the node_osm_id up here, 96:02 with variables that we will fetch 96:06 from based on whatever the user has selected. 96:08 And, whatever page that they're rendering. 96:14 Okie-doke, looks like we've got an error here, 96:23 for photos. 96:28 Something timed out. 96:31 How I wonder if, let's do a gatsby clean. 96:40 And, the gatsby develop again. 96:41 I wonder if there was some issue 96:44 with a timeout with the Mapillary API perhaps. 96:49 We could better rebuild. 96:54 Maybe we hit our rate limit on the Mapillary API. 97:01 Okay, so while that's building, 97:03 let's start working on our GraphQL stuff. 97:14 So, we now should be able to import useLazyQuery. 97:27 So this is the Apollo Client hook we wanna use 97:30 for manually executing a GraphQL query. 97:35 And, we want the gql template tag 97:38 for parsing GraphQL queries from Apollo Client. 97:45 And then, let's also define our GraphQL query. 97:55 We'll wrap that in the gql template tag, 98:01 that's going to parse it. 98:04 So, basically, what we want is this query here. 98:08 But, we are going to add some GraphQL variables to that. 98:16 So this is going to be, 98:18 let's call this getRoute. 98:21 And this is the syntax we use 98:23 to define GraphQl variables. 98:26 So, we're gonna have a from variable, 98:29 that's going to be a PointOfInterest ID and a to. 98:34 It's also gonna be a required ID type. 98:38 And then, we're gonna call the PointOfInterest field. 98:42 And, we're gonna look up the node_osm_id. 98:46 That's gonna be the from, 98:48 so whatever, pay whatever detail page, 98:51 pointers are we're currently looking at. 98:52 That's going to be the. 99:04 I think my headphones clicked off there. 99:07 It looks like they're still on. Okay. 99:10 Holla if you can't hear me for any reason, 99:12 but we're crystal good. 99:15 Okay, so routeToPOI, 99:17 that's the field 99:19 that we wanna select on the PointOfInterest. 99:22 Passing now the to PointOfInterest id. 99:27 And, we wanna get back a bunch of latitude and longitudes. 99:35 Okay, cool. So that's the query we wanna run. 99:38 And then, we want to, 99:45 down here we'll say getRoute, 99:53 and then also loading in data. 99:57 So those are the, we have a loading state 100:01 when we run our query, 100:04 will have a value in loading. 100:06 Otherwise, I have the data in data, 100:13 GET ROUTE QUERY. 100:14 Okay, so, we're using the useLazyQuery, 100:17 because remember, we don't want this query to execute, 100:20 when we mount this component. 100:21 We only want to execute the query, 100:23 when a user makes a selection from the drop-down 100:26 and our onRouteSelected change handler gets called. 100:30 There's one problem here. 100:32 That Gatsby passes in a prop called data. 100:35 So, we don't want this to be called data. 100:37 Let's alias this to routeData. 100:44 Okay, so now, 100:46 that means that in our onRouteSelected. 100:50 So, in the event handler for that drop-down, 100:57 we can call getRoute. 101:01 And, the variables we're gonna pass in, 101:03 remember, we need a from and a to. 101:05 Well, the from, this is gonna be the post object. 101:09 So this object here from the data object, 101:13 from this GraphQL query 101:17 that Gatsby runs for us, 101:18 during the build process. 101:26 And then, the to, 101:30 well, that, we're gonna grab from this event. 101:33 So this is the change handler for that select drop-down. 101:38 So, it's going to be e.target. 101:42 That's the element. 101:44 The select element, e.target.value. 101:51 And, for now, let's just log that. 102:01 So, we'll log the routeData. 102:11 Question. A couple of questions in the chat. 102:15 "Is there a limit on properties 102:16 and relationships a node can have? 102:21 And, how can I take text from react 102:23 and search any node in Neo4j with GraphQL? 102:25 I assume I can create a custom query and GraphQL. 102:28 But, not sure how to do a free text search in Neo4j." 102:33 Yeah, great questions. 102:34 So, your first one, 102:36 "Is there's a limit on properties 102:38 and relationships a node can have?" 102:41 I mean not a practical limits. 102:43 There's a format of the file store in Neo4j, 102:47 I think imposes some limit of the number of properties, 102:54 but it's something like, 102:57 I don't know, 10s or hundreds of thousands. 102:59 I'm not even sure what it is. 103:00 So, you run into I think, a data modeling problem, 103:05 before you hit the limit 103:06 of the number of properties you can have. 103:09 There's a separate property store. 103:10 So, all property values are written out, 103:14 in the property store, 103:15 once you get over a certain number of properties. 103:17 That just expands out onto desk. 103:19 So, basically, whatever you can fit on desk, 103:22 is properties that you can store on a node. 103:26 You typically though don't wanna store large properties. 103:30 So, if you have like a image or something like that, 103:38 you probably don't wanna store like a BLOB, 103:42 a Binary Large Object. 103:44 You can store byte arrays in Neo4j. 103:48 But, typically, what you would do instead, 103:51 is like store that image in S3 103:54 or something like that, 103:55 and store a reference to it. 103:58 So, storing large properties, 104:01 is typically not a good idea, 104:02 since Neo4j is optimized for traversing the graph, 104:05 not loading large properties from the property store 104:09 that can, if you have very, very large properties. 104:11 That can sometimes slow down traversals. 104:15 In terms of the number of relationships a node can have. 104:21 I mean, there is, you know, some, like physical limit. 104:27 Well, let me back-up, relationship types. 104:30 So, the number of relationship types that you can define. 104:35 There's some, I don't know, extremely large limits 104:38 that again, you'll probably hit a data modeling error 104:40 before you actually hit it. 104:41 And then, in terms of the actual relationships 104:43 that can be connected to any one node 104:46 or the number of relationships you can store in the graph. 104:48 No, there isn't really a practical limit, you know. 104:51 It basically maps to, you know, 104:54 whatever you can fit on disk, 104:55 in terms of the size of your data set. 104:59 Then your other question 105:00 about taking text and searching with GraphQL. 105:05 Yeah. So, if we look at our PointOfInterest nodes here, 105:18 there's a filter object. 105:21 So, this filter object is generated 105:25 for any type that we define in Neo4j GraphQL. 105:29 And, for string properties, 105:34 it generates these starts with, contains, 105:39 ends with, filters. 105:41 So, if I wanna search where the name contains Statue, 105:53 so show me all the PointOfInterest that contain Statue. 105:59 I can only find one, Alice in Wonderland. 106:04 What about? Might be another one. 106:09 I know tennis. Aren't there a bunch of tennis courts 106:10 or something like that? 106:13 Yeah, so that's one way to do it. 106:14 I can search using these generated filters, 106:18 specific to any properties. 106:20 If you have more custom needs, 106:24 like let's say I want to do a search 106:27 across a bunch of different properties of a node. 106:30 So, maybe I wanna search the name, the type, 106:32 the description for some keywords, 106:35 or I wanna do fuzzy matching. 106:38 Then you can use some 106:39 of the full-text search functionality in Neo4j. 106:42 And, you can expose that 106:44 via a custom query using a cypher directive. 106:48 There's an example in the guides. 106:53 Where is this? 106:55 It's in the docs for custom logic, I think. 107:01 Custom Top-Level Query Fields. 107:03 Yeah. So, Custom top-level query fields. 107:05 So this example goes through, 107:09 how to use the full-text index functionality, 107:13 and expose that to do fuzzy matching 107:16 across a bunch of different fields, 107:23 properties on the node. 107:27 See if type A and D are probably called description. 107:30 Yeah, exactly. Yeah. 107:31 So that's exactly. 107:32 You would use, you'd create a full-text index like this. 107:37 Here, it's just using the name, 107:42 property on the business node. 107:44 But, you can add this. 107:45 You can create the index 107:48 that will include multiple node labels or properties. 107:53 And then, a query to that index, 107:56 will give you nodes that match it, 107:58 even if it's a business. 107:59 Or, let's say we added reviews here, whatever. 108:01 Whatever matches in the index. 108:03 So, yeah, sounds like that's what you want. 108:09 Cool. Yeah, good questions. 108:14 Okey-doke. So, let's test this out. 108:18 Let's choose a point of interest. 108:26 Doesn't matter what we pick. 108:28 Sure, how about the Beethoven Statue? 108:34 Here it is on the map. 108:36 And, let's say we want to route to the great Hill. 108:41 Okay, nothing happened. 108:42 I don't think we expect anything to happen. 108:45 Let, something up the console. 108:54 Did we get an error? 108:57 It's like we got a bad request. 108:58 So, let's. 109:01 Yeah, we did. Create HTTP link. 109:06 Bad request. 109:09 Because, yeah? No? That's right. 109:17 Let's take a look in the Network tab. 109:21 So, we got a 400 bad request. 109:27 What is a variable from? 109:29 A required type ID was not provided. 109:34 So, if we look at the query that we ran here, 109:40 it's kind of hard to read. 109:41 We ran, yeah, a getRoute query, 109:44 but then in our variable object that we passed, 109:47 it had a to object, 109:50 but it did not have a from object. 109:59 But, why is that? 110:00 Well, our from, to find here. 110:03 So, we said, post.node_osm_id. 110:08 So, here's our post object. 110:10 That's the current PointOfInterest. 110:15 Let's look at the GraphQL query. 110:19 Oh, we're not giving the node_osm_id. 110:21 So, node_osm_id is not included in the selection set 110:26 for the PointOfInterest on the detail page. 110:34 So, if we add node_osm_id, 110:37 then that should now be defined here, 110:44 when we pass that query here, 110:51 when we're building up the variables objects 110:53 to pass to the useLazyQuery. 110:59 So, because we changed the GraphQL API, 111:02 or the GraphQL query, 111:05 Gatsby needs to rebuild all our pages. 111:09 So, while we're waiting for that to build, 111:13 let's think about, 111:16 we know we're gonna have, 111:20 let's look at previous queries. 111:27 This is the route. 111:29 Yeah, it's the route. 111:32 So, if you look at our route query, 111:35 we're going to have data of this format. 111:38 So, a bunch of these latitude, longitude objects. 111:43 And, to render that on the map like this, 111:49 we need to transform that into GeoJSON. 111:54 So, we want to think of how we're gonna do that. 112:01 I guess what we'll wanna do is, 112:06 let's define a variable. 112:09 This is gonna change, so we'll use a let. 112:13 And then, we'll say, 112:14 "If we have routeData," so routeData. 112:20 This is only defined, 112:23 so now we're using this useLazyQuery Apollo Client hook. 112:30 So, routeData here, 112:34 this is only gonna be defined, 112:36 after we've actually successfully fetched some data. 112:40 So, we'll say, 112:41 "If we have routeData, 112:46 well, then routeGeojson is going to be some object, 112:53 in the format that we want, 112:55 is just following this example." 113:02 This is a valid GeoJSON, 113:05 which is a specification 113:06 for defining geometries using JSON. 113:13 So, feature and the geometry is of type LineString, 113:29 and coordinates. 113:30 So, here type LineString and then coordinates. 113:33 That's gonna be the route that we wanna render. 113:42 Well, this coordinates is a list of lists 113:50 or a array of arrays, 113:52 where the first one, 113:55 the first elements in the nested array, 113:59 is longitude and the second is latitude. 114:04 But, we have something that looks like this. 114:08 So this is going to be routeData. 114:15 So, following format, .PointOfInterest. 114:20 And this is a list field. 114:23 So, it's like the first one here. 114:27 .routetopoi. Right? 114:31 So, here, this, probably just following, 114:33 this selection set structure, routetopoi. 114:41 And, we're gonna, 114:41 and this is now our list of latitude, longitude objects 114:44 and we wanna convert each one of these objects. 114:48 So, latitude, longitude, as an object. 114:51 We wanna convert that to this, to a array, 115:00 where longitude is the first element, 115:04 latitude is the second. 115:06 So, we'll map over that. 115:12 And, return, an array, s.longitude, s.latitude. 115:26 So, longitude. 115:34 And then, let's lock that. 115:49 Okay, so now, we go over to the Beethoven statue. 115:58 Where is it? Here it is. 116:00 Got our Marker and wanna route to the Zoo School. 116:08 Well, nothing has showed up on the map. 116:11 But, if look in the console here, 116:21 we're logging something from line 57. 116:23 That's here. Cool. 116:25 So, we've logged this GeoJSON object. 116:29 So, type, feature, geometry, type, 116:32 LineString, coordinates, 116:34 and then we have a bunch of coordinates. 116:39 Cool, so that looks like a route. 116:43 That is good. 116:46 Now, we wanna add that to the map. 116:50 Let's go back to our documentation, 116:52 looking at how we show this LineString on the map. 116:59 We've got our GeoJSON object now. 117:01 So that's good. 117:03 So, we wanna create a Source and a Layer. 117:07 So, let's look. 117:09 Where's our map? 117:09 Here's our map. 117:12 We defined our Markers, so that's good. 117:19 After the Marker. 117:21 So, first, let's check for routeGeoJSON. 117:25 So, only in the case, 117:26 that we actually have called our Neo4j GraphQL API, 117:32 gotten the routes, 117:33 we've converted to GeoJSON. 117:34 So, only in the case where we have this GeoJSON object, 117:37 we wanna render the Source and the Layer component. 117:40 So, let's check that that is not null first. 117:45 And then, let's render, 117:48 wrap this in a div I guess. 117:51 So now, we'll render a Source here, like this. 117:58 Over here, Source component. 118:01 Let's call this route. 118:03 It's going to be of type, GeoJSON. 118:07 The other types, I think you can, 118:09 you can pull in remote Sources as well. 118:12 So, I could like hook this up to like a GeoServer 118:18 or some Mapbox service 118:20 or something like that, 118:21 to pull in remote Sources. 118:24 But, it's gonna be geojson, the data. 118:27 It's going to be this routeGeojson object. 118:33 And then, we need a Layer. 118:36 And, the layer id is route. 118:48 Let's format this, 118:49 so it's a little more readable. 118:51 There we go. 118:52 Okay. So, on our Layer, what else do we have? 118:56 We have a type, a source, a layout. 119:00 So, let's add those things. 119:03 So, type will be line, 119:07 the source is gonna be route. 119:10 So that's the name that we gave the source here, 119:13 that is bound to our geojson object. 119:18 Then, the layout. 119:23 So this is some styling here. 119:25 So, I'm just copying what we have from our example, 119:29 in the React Map GL docs. 119:36 So, a line-cap is round. 119:43 And, what else? 119:45 Paint, that's how we wanna style that line-color. 119:53 Let's do blue so it stands out a bit. 119:57 And, line-width, we can do eight. 120:01 That's what this example uses, 120:03 that's pretty visible, it should be fine. 120:08 Okay, so that should render our route. 120:14 Let's also add a Marker. 120:18 So, we had a, like a starting point, green Marker. 120:24 So, let's also now render a Marker. 120:33 I'm just gonna copy this one. 120:38 We could probably move some of this out, 120:40 into separate react components. 120:44 So, we don't have so much logic in this one. 120:48 Something to think about. 120:52 Okay, so now, we have a Marker. 120:54 Instead of green, let's make this one red. 120:57 And, for the latitude and longitude, 121:04 instead of where the starting point is, 121:09 which is the PointOfInterest 121:12 that we're showing for the detail page. 121:14 We want the last element 121:17 of this GeoJSON coordinates array. 121:22 So, the last latitude and longitude point. 121:29 How do we get that? 121:30 Well, that's gonna be routeGeojson.geometry.coordinates. 121:41 And then, I need the index into that. 121:49 I need to know the size of that array, I guess. 121:53 So, it gets a little awkward. 121:57 I could write a function to extract this. 121:59 But, let's see if this with work. 122:01 routeGeojson.geometry.coordinates.length, minus one, 122:13 should give us the last, 122:15 since we're doing zero-based indexing. 122:17 And then, I want the first element 122:23 to give me the longitude. 122:27 And then, exactly that. 122:38 Exactly that. 122:39 Will copy paste that, 122:41 but the second element will give me the latitude. 122:49 Okay, I think I should format that. 122:54 It's a bit more readable. 122:55 I think that that should do it. 122:59 Let's see what happens. 123:02 So, here we are at, 123:05 let me zoom out a bit. There we go. 123:08 So, here we are at the Beethoven statue. 123:12 And, let's say we want to go 123:13 to the Three Dancing Maidens statue. 123:19 Cool. There we go. 123:20 So, here's the route that we get. 123:25 If we zoom in, 123:27 we can see the path that we're following. 123:30 So, there's this footpath here. 123:34 You'll notice that, 123:36 this isn't exactly following the geometry of these paths. 123:41 And, the reason for that, 123:42 is the routing graph that we've built, 123:45 on top of the OpenStreetMap layer, 123:48 only includes the intersections. 123:51 So, we don't need to make a decision 123:55 at every single. 123:59 They're called OSM way nodes in the data model. 124:04 So, the OSM node is every single like observable point 124:09 that someone has mapped in OpenStreetMap. 124:12 So, for the routing graph, 124:14 we're only routing between intersections. 124:16 So, you'll notice some things 124:17 that don't quite follow the outline of the path. 124:21 That's because, we're just basically drawing a straight line 124:25 from one intersection to another. 124:27 So this footpath intersects here 124:30 and then it also intersects this other footpath here. 124:32 So, we just draw a straight line, 124:33 between those intersections. 124:35 But this I think, is good enough to give us an idea 124:37 of what path we're supposed to take. 124:41 So this is the A* shortest path. 124:46 Going along these footpaths, 124:49 and then we get up on, 124:50 looks like we get up on Fifth Avenue 124:53 to go on a straight shots, past the museum. 124:59 And then, it's Neo. 125:00 It knows exactly how to get through these footpaths here. 125:05 They go in a circular area around this fountain. 125:11 Cool. That looks fun. 125:15 Let's select another one. 125:17 How about the Duke Ellington Memorial? 125:23 Oh, that one. 125:25 That one was very close to the one we just did. 125:28 That's very similar. 125:32 But, something down here. 125:33 What's down there? 125:35 Maybe, the Worthless Boulder. 125:39 I don't know where that is. 125:40 Oh, again, that's on its way up there as well. 125:44 So, you can see here's a path 125:47 that's cutting across the water. 125:49 But, again, this is more, just because of the intersections. 125:53 Only that we're plotting. 125:55 We're actually taking this footpath around. 125:59 Cool. Let's try this for some other points of interests. 126:04 Let's see, how about, 126:11 here's the Dancing Crane Cafe. 126:14 So, from the Dancing Crane Cafe to the Hernshead. 126:23 Let's see, let's make sure we go around the water 126:26 for that one. 126:30 Where was I? 126:30 This Dancing Crane Cafe, which is here. 126:35 And, where did we say we wanted to go? 126:37 To the Hernshead. 126:40 I guess these aren't in any particular order. 126:42 Here's the Hernshead. 126:47 Yeah, and, so that routes us along this path. 126:52 And then, along this path, along the water. 126:57 And then, coming in on this footpath. 126:59 Again, this is the actual path we're taking, 127:02 but we only see the intersections. 127:07 Buday says, "That's route to the Loeb Boathouse." 127:12 Yeah, where is that? Is that on the reservoir? 127:13 And, actually no. 127:14 Let's, let's see, where's the Boathouse? 127:21 The Blockhouse. 127:29 Yeah, we should put these in alphabetical order, 127:32 or something like that. 127:37 Here's the Azalea Pond. 127:39 Actually, we can. 127:41 Let's put these in alphabetical order. 127:42 How would we do that? 127:43 Well, to do that, PointOfInterest. 127:51 Well hey, let's do it in here, 127:52 so we can see what it actually looks like. 127:55 We would say, order by name descending. 128:09 Or, actually, I guess what we want is ascending. 128:12 Probably makes more sense for alphabetical. 128:16 So, we add an orderBy, name, ascending order. 128:24 So this allPOIs, 128:26 this is what we're using 128:30 to fetch these PointOfInterest. 128:34 So, we've ordered those in ascending, 128:38 which should make it easier to find things. 128:43 Code Lumberjack says, 128:44 "Hey, I enjoyed your talk at NODES." 128:46 Cool. Thanks. 128:49 Thanks for coming to NODES. 128:51 And, watching our talk. 128:54 Glad you enjoyed it. 128:55 I think we looked at this earlier, 128:57 but I didn't specifically call out my talk. 129:00 So, since you brought it up, 129:02 we can do some shameless self-promotion here. 129:05 So, NODES was our online conference, 129:07 that was a couple days ago. 129:09 If you go to the site, now, 129:11 you'll see that the content hub is linked. 129:15 And these are all of the recordings in all the slides 129:20 from the talks. 129:23 And, I gave a talk, 129:28 called Low-Code GraphQL APIs with Neo4j. 129:36 I'll drop the link, 129:38 to this here in the chat. 129:39 For anyone who missed that, 129:43 there's a lot of things that we covered in here. 129:47 Kind of the high level goals 129:50 for the GraphQL integrations. 129:54 And then, we take a look 129:56 at a GraphQL Architect specifically. 130:00 So, if you haven't used GraphQL Architect, 130:02 it is a graph app for Neo4j desktop. 130:10 We played around with it a bit on the stream. 130:13 But, it makes it easier to build. 130:16 GraphQL API is backed by Neo4j, 130:18 just within the Neo4j desktop, 130:19 without writing any code. 130:21 So, if you wanna be able to, you know, 130:24 say with one click, 130:25 get a GraphQL API on top of your Neo4j database. 130:28 This is the tool for that. 130:30 So, I'll drop some links in the chat 130:33 for resources that point to that. 130:36 And then, the other thing I mentioned in the NODES talk, 130:42 is that we're going to be doing, 130:48 Fullstack GraphQL book club, 130:50 next week on the chat. 130:55 So, if you wanna participate in that, 130:58 download this free ebook. 131:01 This is the first few chapters. 131:05 There's three chapters, 131:06 it skips chapter two but that's fine. 131:08 We'll still do that in the book club. 131:10 And, we'll start next Thursday on the stream, 131:13 doing the exercises from that. 131:17 Anyway, sorry for the distraction there. 131:20 You got me thinking when you mentioned NODES. 131:24 Okay, so back to now, our app. 131:27 So now, things are in alphabetical order. 131:30 So, we can more easily find the Loeb Boathouse. 131:35 So, here's the route 131:36 from where we, the Dancing Crane Cafe 131:41 to the Boathouse. 131:44 Which I think this is where you see the iconic pictures 131:49 of, you know, couples romantically 131:52 and rowboats in Central Park. 131:55 Is that why you rent them? Maybe? I don't know. 131:59 Okay, cool. 132:00 So that I think is everything, 132:03 we wanted to add today, anyway. 132:06 But, a bit of a long stream. 132:08 So, thanks for the folks that stuck in there 132:12 and got everything completed. 132:15 Just a recap of what we did. 132:18 We added this map. 132:21 So, on the index page of our Gatsby app, 132:24 we added this map, 132:26 with a Marker for every point of interest 132:29 that we have in the list below, 132:32 with a detailed view. 132:34 So, I can see for each of these, 132:40 what it is, 132:41 What type of point of interest it is? 132:43 A link to the detail page. 132:44 And then, if we have an image from Mapillary, 132:47 we have that here. 132:48 And then, we added this other map, 132:52 which shows us initially a Marker 132:55 for wherever that point of interest is. 132:57 Then, we added Apollo Client to our Gatsby app. 133:02 So that now, instead of running GraphQL queries 133:05 against the Neo4j GraphQl API at build time, 133:09 we're running them live 133:11 when a user selects a point of interest. 133:16 And, we're using the A* graph algorithm 133:20 to find the optimal route 133:24 from one point of interest to another 133:27 and then rendering that on the map. 133:34 And, see that. We used Mapbox GL JS 133:38 and specifically, the Rebekah react Mapbox wrapper 133:48 for Mapbox GL. 133:52 Cool. So, I think we'll call it good there. 133:57 Thanks again, everyone for joining. 134:00 I hope that was fun. 134:03 And, like I said, next week on the stream, 134:06 we will pick up with chapter one 134:09 of the Fullstack GraphQL book club. 134:13 So, if we look at what that is, 134:17 https://grandstack.io/book 134:23 Here's the table of contents. 134:27 So, chapter one, what's the GRANDstack? 134:30 So, chapter one is more of an overview chapter. 134:34 But, it should still be interesting. 134:35 There's still some exercises in there 134:37 that we'll do on the live stream next week. 134:40 But this gives an overview of what GRANDstack is? 134:43 How the pieces fit together. 134:44 Why should we use these components? 134:47 Why should we use them together? 134:49 That sort of thing. 134:50 So again, here's the URL to download the book, 134:55 if you want to participate in book club. 134:58 I guess, you don't need to do any homework for this, 135:01 if you don't need to. 135:02 We'll do the exercises together. 135:04 But, anyway, hope you can join next week as well. 135:08 I will push up the code for today 135:12 to the GitHub repo, which is here. 135:17 And, you can also find the recording. 135:20 I'll link it here, 135:21 but also be on the Neo4j YouTube channel. 135:26 Cool. So, with that, we'll call it a day. 135:30 Thanks for joining 135:31 and we'll see you next time. 135:32 Cheers.
Subscribe To Will's Newsletter
Want to know when the next blog post or video is published? Subscribe now!