Awwws! Serverless, GraphQL and Neo4j.

Deploying a Real-Time Routing Serverless GraphQL API Using GRANDstack And Serverless Framework

--

The future is meow. 😸

Let’s explore it together. Today we are gonna take a look at AWS Serverless and using AWS Serverless to setup our GraphQL API and attaching it to a Neo4j Sandbox.

That cute kitteh likes code, if you like code too here is a REPO

For those of you that enjoy moving your eyes from left to right (I’d include right to left and up and down, but I’m just learning my second latinate language) and ascertaining meaning from the data gathered through this activity, let’s roll on. Feel free to reference the repo as we ditch the server and say hello to lambda.

First off we are going to need a CLI, don’t worry in the future of the future we won’t need that either, but today we are are just in the future.

$ npm install serverless -g

Bootstrap your app

After that is installed you can use it to bootstrap your application with a vengeance. Run serverless and answer some questions.

Serverless doin’ its thing.

Once you’re done you’ll have a folder to cd into and check out. It’s missing a few things so let’s add those in. For starters, JavaScript needs some new clothes. Let’s cast around the internetz and see what we can steal. Thankfully there is a serverless plugin for TypeScript and it has an example.

If you are following along you can rename your handler.js file to handler.ts and scoop the contents of the example into your shiny new TypeScript file, we are going to make a few other changes here, but for the most part you can match up the dependencies.

$ npm install @types/node @types/lodash serverless-plugin-typescript --save-dev
$ npm install lodash typescript --save

Update for TypeScript

Include typescript as a dependency is necessary, even though the Prisma example does not have a reference to it. Without it our plugin won’t operate correctly. They probably had it install globally or lower down or something, all good 😄. Then we can edit our yaml file with the new attributes:

Deploy

It is time! run serverless deploy and see what is… Serverless should spit out a url for you and if you go to it, it will return some data. Something like this, with a bunch more input data:

{
message: "Go Serverless v1.0! Your function executed successfully!",
...
}

If something went wrong you can always clone the repository, checkout to the typescript-first branch and compare. If see a way to improve the initial setup open an issue or start a conversation. 🎣 ✊

It’s pretty rad to push those functions up and see them run on our AWS cloud function, but we need a quick development cycle so let's install serverless-offline as a dev dependency.

$ npm install serverless-offline --save-dev

We’ll need to add this serverless plugin to our yaml file and as the typescript plugin helpfully points out, it needs to be referenced below our typescript plugin. We can check to make sure that our plugin is working by running:

$ serverless --verbose
Output of serverless — verbose
You should see the offline plugins help text

Sweet it’s there. Let's run it:

Now we have a local simulation of our cloud lambda functions

Apollo Tho

Dope, we got a local server, an AWS app and it’s all in TS, which is chill. Next, let’s talk GraphQL. Apollo server, a very useful piece of the GRANDstack architecture has a lambda library that we can use to set up our AWS interpretation. Let’s install that npm package as well as graphql.

$ npm install apollo-lambda-server graphql --save

Let’s start with their basic GraphQL example and see if it works. This is pretty minimal and setup to test our configuration rather make any major leaps into uncharted GraphQL space. We’ll add a new file called graphql.ts and drop this static string resolver pack in there.

Serverless isn’t aware that you’ve added a new handler and that you want it to run that handler on specific routes. Let’s key that in. Underneath your hello world route we’ll add a GraphQL hello world route, it needs to have both post and get methods to allow the playground and resolvers to work correctly.

We run serverless offline and try out our query. If you aren’t seeing this, checkout or take a quick look at the graphql branch

A very simple query

Kittehs in the Neo4j sandbox

Head on over to Neo4j and spin up a new blank sandbox to hold your data. You are going to need something in there, today we are going to live dangerously and add a whole bunch of kittens. We are after all going for maximum awwws. Open the sandbox browser and run a few of these commands:

$ CREATE (n:Kitteh { name: 'Fluff Kitteh', cuteness: 45 })
$ CREATE (n:Kitteh { name: 'Bro Kitten', cuteness: 31 })
$ CREATE (n:Kitteh { name: 'Cuddlez McCuddleFace', cuteness: 136 })
$ CREATE (n:Kitteh { name: 'Lil Meow', cuteness: 9001 })

Now we have some Kittehs! Much awwwws. But Oh Noes! 😱 They are trapped in the database! We have to use a our typedefs and resolvers to find them. The kittens are counting on us!

Let's get our npm packages first.

$ npm install neo4j-driver neo4j-graphql-js --save

neo4j-driver is going to allow us to connect our Apollo server to the lambda functions. neo4j-graphql-js will generate a GraphQL API based on our typedefs without writing resolvers.

Yeh! after a serverless offline run we found our kittens. Now it’s time to send them up to the cloud. Run serverless and see what happens. If you get stuck you can check out the neo4j branch

A slightly more advanced example

We got some Kittehs, which is very cute, but not super useful, sorry kittens. 😿 Let's dive a little deeper and check out the rest of the owl.

First off we need some data to work with, thankfully Neo4j has our back on that one. Mapping is fun, right? Let's cut into a OpenStreetMap use case in a shiny new sandbox environment. For now, you’ll need to use this URL to unlock the OpenStreetMap Neo4j Sandbox which comes pre-loaded with a dataset of OpenStreetMap of Central Park in New York City in Neo4j: https://sandbox.neo4j.com/?usecase=openstreetmap

You’ll need to update your credentials once you get that one going. If you like you can open a browser instance and check out all the various nodes. A quick look will give you some points of interest:

$ MATCH (n:PointOfInterest) RETURN n LIMIT 25

While you are over there we are going to do a quick update so our Cypher queries later on can handle the data correctly. The open street maps ids are currently stored as integer types in the database, but we want to treat them as ID fields in GraphQL. So let’s cast all those to a string:

$ MATCH (p:PointOfInterest) SET p.node_osm_id = toString(p.node_osm_id)

Next, we need some typeDefs that match up. We could type them up one at a time, but let's be honest: you steal code, I steal code, we all steal code. A bit of searching around and you should be able to track down William Lyon’s Central Perk API repo. We will steal, in a friendly way, these great typeDefs that include some very nice Cypher graphql schema directives.

Alright, let's break that down a bit, we have three new types attached now that allow us to do some pretty in-depth stuff.

Wikipedia Query

First, we can run wikipedia that leverages some of the connected apoc functions that come standard in new sandbox. If you are starting from scratch on desktop you will need to install apoc

{
PointOfInterest {
wikipedia
}
}

This loads the Wikipedia record associated with each map data point. This information could be loaded into an explanatory panel on hover or parsed more completely to add data to points of interest.

Tags Consolidation

Next we can pull down some of the open street maps tags and consolidate them in a single query. We could use these to organize our map points or setup a user search/filter.

{
PointOfInterest {
tags {
name
}
}
}

Routing Query

And last but not least is the routeToPOI, definitely the coolest of the pack. Here we are going to use one of our other superfriends: Neo4j Graph Data Science Library.

{
PointOfInterest( name: <starting_point_name>) {
routeToPOI(poi: <destination_osm_id_as_string> ){
latitude
longitude
}
}
}

This is going to return an array of lat/long points that have been run through our data science library to find the shortest path from point A to point B. Somewhere that’s gonna make a traveling salesman happy. Thank you, Arthur Miller.

This last query opens up the door to some great mapping possibilities, if we dig a little deeper into the Gatsby repo from before there is a pretty nice example of using this mapping library to use that route array of points to track a path.

Where do we go from here?

We all know there is a difference between knowing the path and walking the path and if you don’t know, now you know. We have these data and we have these functions, but why do we care? We’ll let’s you and I take a minute and think about that for a second. Open up your Neo4j browser and let’s run a few commands.

MATCH (a:PointOfInterest) WHERE a.name = "Duke Ellington Memorial"
MATCH (b:PointOfInterest) WHERE b.name = "Jacqueline Kennedy Onassis Reservoir"
MATCH p=shortestPath((a)-[:ROUTE* .. 200]-(b))
RETURN p

If you are a bit confused by this query, don’t worry I am too, there is definitely some magic going on here, right at the 3rd MATCH query which is really one reason it’s so cool. Because those relationships are built into the structure of the database and the database has it’s data science library attached to it it will look at all the possible routes and build a path from a to b.

There are a lot of industries that rely on points a and b and the shortest path between those two and maybe more down to earth, there is the possibility of going for a walk in the park.

With our serverless lambda functions we can setup up a reflection point that will organize our data and with a bit more frontend magic we can throw together a mapBox instance on gatsby, or next, or whatever you are into these days and setup some change listeners for our data.

And there you go: a walk in the park. We’ll get from our point a to our point b if only we walk long enough. 😸

Now, let’s have a tea party.

--

--