#686

Rebuilding the blog... again

I mentioned in a previous post that I had rewritten my blog. I wanted to talk about the tech behind that. So here we go.

Before we get too deep into this article though: I don't normally run ads, but hey, I wrote a book, buy it, please? Real World SRE on Amazon or at Packt.

A while ago I turned this blog into a React app with a GraphQL backend. The downside of that is I couldn't get server side rendering (SSR) working, so all of my google search results and twitter embeds broke. So I decided to start over with two technologies:

  • Next.js
  • Apollo + GraphQL

The app was then refactored into two applications: icco/writing and icco/GraphQL. Both of these are very much in progress, so this is just a snapshot of their current state.

Backend: GraphQL

GraphQL is a web query language developed by Facebook. Its homepage is https://GraphQL.org/. Facebook is actively evolving the spec, as of today they are working to releasing a new version, but the current version is June2018, and all versions can be seen at Facebook's https://facebook.github.io/graphql/. The spec is pretty technical, but the majority of what it describes is that a GraphQL server defines a schema, which can then either be queries to retrieve data, or mutated, to modify data.

An example schema might be something like:

schema {
  query: Query
}

"""
The query type, represents all of the entry points into our object graph.
"""
type Query {
  "Returns a single post by ID."
  post(id: ID!): Post
}

"""
A post is an individual post in the blog.
"""
type Post {
  id: ID!
  title: String!
  content: String!
  datetime: Time!
}

scalar Time

To query this schema (which is a subset of my blog schema, which we'll get into in a few), you could write something like this:

query {
  post(id: "680") {
    id
    title
  }
}

This is just the interfaces that are common to any GraphQL server and client. You can write your GraphQL server in whatever language you want, although most examples are written in Node.js. I wrote mine in Go. When you send a query to a GraphQL server, it's sent over HTTP, and then parsed, and returns it's response in JSON. For the above example, my server returns:

{
  "data": {
    "post": {
      "id": "680",
      "title": "2017 Year in review"
    }
  }
}

You can play around yourself at graphql.natwelch.com, which has the schema schema.graphql.

Every GraphQL framework is different, but generally they work similarly. There is a parser that receives the query. Once the parser knows what data is being collected, the resolver goes and fetches the data and returns it. Finally, the data is reassembled into what the user wants and sends it back as JSON.

I decided to use gqlgen as my framework. I decided on this mainly because it was schema first (you write the schema, and then the code), and it seemed to support the most features of the Go GraphQL servers I could find. Setup of this framework is documented on their site, https://gqlgen.com/getting-started/, but in general, you write the schema, and then run gqlgen to generate all of your code except for resolvers.

A resolver for the above post() method might look like resolver.go#L116-L128. Basically just a function that goes to the database, gets the data, shoves it into an object and returns it. If you had an ORM, you could probably make this code even spiffier.

Usually GraphQL queries are sent over HTTP POST, because most servers don't limit request body size in any meaningful way. This is useful for complex queries. An example is the query for https://www.topic.com/fear-itself which is 18k. This differs from a lot of traditional applications that have separated their frontends and backends. Traditionally, pure JS frontends would make many requests to many different API endpoints, each returning different data. This is fine, but each time the data shape changed, you'd either need to roll out a new endpoint, or block a new frontend rollout on a backend change. There is also a limitation on most browsers that not more than six in progress requests can be made at any one time over HTTP1.1, which also slowed full page renders.

The downside of GraphQL is the caching, which people are still trying to solve. One of the more popular ideas going around is to use Automatic Persistent Queries. Apollo's (the originator of the idea, and a popular Javascript GraphQL library) implementation of this, apollo-link-persisted-queries, is by far the most prominent. The idea is that a client just sends a query name and hash to a server, and if the server has seen that hash before, it just returns the response from cache. Otherwise it tells the client to retry with the full query. Since these requests are going to be small, you could also send them as GET requests, and take advantage of client side or CDN level caching.

This gets to why I picked Apollo as a client. The Apollo team is making the most advances in the GraphQL community. They are trying lots of different things with GraphQL extensions, such as passing back resolver trace data to the client and the aforementioned persisted queries.

It should be mentioned that I'm not using persisted queries on this blog, as gqlgen does not support it yet. Because of that, this blog makes far more queries than it should when there is a spike of people browsing.

Frontend: Next.js

So, my frontend skills have never been great, and as the frontend community has gotten more sophisticated it has slid farther and farther from me. I know enough React to make components that kind of work, but some of the more complicated things, I tend to do a bunch of copy-pasta to get stuff to work. Sometimes I'll dig in deep, but things change so often, I feel like every time I return, everything has changed.

To combat this, I tried out Next.js. So far it's been pretty great. I chose it because it handles the majority of the React pipeline and makes informed decisions about the javascript world that I just don't have enough information to make. Next's documentation isn't that great yet, but it works pretty well, so that hasn't been too big of an issue so far. Thankfully, next has a wonderful examples directory

I followed a bunch of tutorials and got Next and Apollo playing nicely on the frontend, and got server side rendering working, which fixed most of my issues with Google Search. I still haven't coded up open graph html, so Twitter and Facebook cards don't work, but now they will work whenever I add them.

If you've got that open source itch, feel free to add them. More details at icco/writing Issue #13.

Future

Those are the basics of the service. Currently I am in the process of moving the post content editing process from being a magic route on the backend proxied by the frontend to be almost fully client side. When I say magic route, I just had a traditional website for editing content hosted on the GraphQL server. In this new world, authenticated users will send GraphQL requests to modify content. The backend work is done, most of the work is on the client side.

I haven't quite figured out the best practices around sending mutations from Apollo, but I have auth working using JWT and Auth0, which is great.

I hope in the future this will allow me to easily add comments to this blog as well.

End

Hope you found that informative. Hit me up on twitter @icco or via email if you have any questions.

/Nat