GraphQL APIs for Developer Communities
Going Social with the GRANDstack starter
Using the GRANDstack Starter With GitHub, MeetUp, StackOverflow and Twitter data.
The GraphQL Community Graph
As you might have read before, we’re gathering the public activity of the graphql community in a Neo4j graph database. I also showed how that data can be used to find awesome female engineers in the GraphQL community.
GRANDstack Starter
The GRAND stack combines GraphQL, React, Apollo, and the Neo4j Database.
With the GRANDstack starter project you get up and running with an API back-end and a React UI front-end in minutes.
Using the starter, our community data can be easily turned into a React app. All the steps that William Lyon outlined in the video stay the same
The three things that change are:
- the GraphQL schema
- the Neo4j server address
- the GraphQL query + react code
I tried to keep the changes minimal so that you can just use the existing starter project and change the three files.
I quickly go over the example for StackOverflow, here are the files for Twitter, Meetup, and GitHub.
StackOverflow — GraphQL Schema
The GraphQL schema uses currently SOUser
, Question
, Answer
and Tag
with the data that is currently collected. I had to add the SOUser
label to he data as neo4j-graphql-js
doesn’t allow tagging secondary labels on types which would be a nice feature.
If you ever used StackOverflow you should be able to work with this schema immediately.
graphql-schema.js
import { neo4jgraphql } from "neo4j-graphql-js";
export const typeDefs = `
type Link {
url: ID!
}
type SOUser {
id: ID!
questions(first: Int = 10, offset: Int = 0): [Question]
@relation(name:"POSTED", direction:"OUT")
answers(first: Int = 10, offset: Int = 0): [Answer]
@relation(name:"POSTED", direction:"OUT")
reputation: Int
name: String
location: String
profile_image_url: String
}
type Tag {
name: ID!
tagged(first: Int = 10, offset: Int = 0): [Question]
@relation(name:"TAGGED", direction:"IN")
}
type Question {
id: ID!
title: String
link: String
score: Int
text: String
closed_date: Int
closed_reason: String
favorites: Int
view_count: Int
comment_count: Int
is_answered: Boolean
created: Int
updated: Int
answers(first: Int = 10, offset: Int = 0): [Answer]
@relation(name:"ANSWERED", direction:"IN")
author: SOUser @relation(name:"POSTED", direction:"IN")
tags: [Tag] @relation(name:"TAGGED", direction:"OUT")
}
type Answer {
id: ID!
text: String
comment_count: Int
is_accepted: Boolean
created: Int
score: Int
question: Question @relation(name:"ANSWERED", direction:"OUT")
author: SOUser @relation(name:"POSTED", direction:"IN")
}
type Query {
users(id: ID, name: String,
first: Int = 10, offset: Int = 0): [SOUser]
questions(id: ID, title: String,
first: Int = 10, offset: Int = 0): [Question]
tag(name: ID!): Tag
}
`;
export const resolvers = {
Query: {
users: neo4jgraphql,
questions: neo4jgraphql,
tag: neo4jgraphql
}
};
Neo4j Server Settings
This uses the publicly available community graph Neo4j server (currently running Neo4j 3.3.5) with a read-only user.
Update your .env
file to this:
NEO4J_URI=bolt://107.170.69.23:7687
NEO4J_USER=graphql
NEO4J_PASSWORD=graphql
GRAPHQL_URI=http://localhost:4000
Please adapt the GRAPHQL_URI
when you deploy as described in the Readme of the api
module.
With that in place, our API backend can restart and we can check out the data in the playground at http://localhost:4000
.
GraphQL Query & React Code
I wanted to keep the changes of the starter really minimal, but I’m also a React noob, so please bear with me.
I changed the query to also include a few questions and their score
and nested tags
names. That GraphQL query then runs as a single 3-hop-graph query in Neo4j. (:SOUser)-->(:Question)-->(:Tag)
UserList.js
import React from "react";
import { Query } from "react-apollo";
import gql from "graphql-tag";
import './UserList.css';const UserList = () => (
<Query
query={gql`
{
users(first: 10) {
name
questions(first:3) { title, score, tags {name} }
}
}
`}
>
{({ loading, error, data }) => {
if (loading) return <p>Loading...</p>;
if (error) return <p>Error</p>; return (
<div className="UserList">
<h1>Users:</h1>
<ul>
{data.users.map((u,i)=> (
<li key={i}>{u.name} <ul>{u.questions.map((q,i) => (<li key={i}>{q.title} ({q.score} {q.tags.map((t)=>t.name).join(", ")})</li>))}</ul></li>
))}
</ul>
</div>
);
}}
</Query>
);export default UserList;
The pretty boring UI looks like this. But fortunately in our Hackathon you can do much better :)
With these 3 minimal changes you can now run your GRANDstack starter against our “social graph” data.
So please participate in the GRANDstack Hackathon that still runs until June 30th and let your submission win a “Brik Laptop Covers” in many different sizes. Even with bricks for your favorite logos.
Good Luck! We’re looking forward to your submissions.