Enjin Cloud

Enjin Cloud

2019 – 2020See live
Steve on X

Enjin build a suite of user-first blockchain products that enable game developers to easily create, manage, distribute, and integrate blockchain assets. I worked on their Laravel and Vue SPA and GraphQL APIs for 14 months, about 1,800 commits, mostly making the API faster to work with, safer to change and possible to test.

Enjin Cloud

Enjin GraphQL API

Enjin's GraphQL API is critical to their mission of putting blockchain tech in the hands of game developers, allowing them to build both client and server-side integrations into their games.

When I started working on Enjin Cloud there was already a v1 API in place, but it had a few problems:

  • Authentication, access token handling and app selection needed a rethink. API consumers could authenticate with either app or user tokens, and end up in a variety of states, with inconsistent access restrictions.

  • Ethereum Parity nodes aren't the fastest things ever, and some queries needed several hits to resolve their fields, so even small list queries were noticeably slow.

  • GraphQL queries often returned long unpaginated results, and nothing logged what clients were actually requesting.

  • Many queries and mutations violated the single responsibility principle, so the user's intent was often unclear, which caused problems for both access control and performance.

I spent most of my time simplifying resolver logic, stripping features from the v1 API, and adding test coverage, so we knew what was possible and whether it worked. I split the API into schemas per audience (wallet, platform, Beam and others) and added pagination. I renamed and deprecated fields carefully, leaving 43 deprecation notices rather than broken integrations. I also built a custom GraphQL Playground with a tab per schema and your apps' credentials filled in.

Enjin GraphQL Playground
The GraphQL Playground

Tracking API usage with Kibana

As I looked into Enjin's GraphQL API it became apparent there were many arguments, fields and queries we'd want to deprecate. The GraphQL spec supports deprecating fields, but not arguments, and any change risks breaking integrations, so we needed to see what clients were actually using in production.

Traditional API logging tools don't help much with GraphQL, so I added resolver middleware support to GraphQL Laravel. Every resolver then ran through a pipeline of authentication, platform locks, pagination, validation and logging, and each query's fields and arguments were sent to Logstash, to visualize in Kibana. I contributed a handful of other fixes to GraphQL Laravel, graphql-php and Laravel along the way.

After I shipped this we were able to find a number of problems behind slow queries, and make data-driven decisions about what we removed.

Kibana
GraphQL usage in Kibana

Giving back to GraphQL in PHP

Most of what we needed from GraphQL in Laravel didn't exist yet, so where it made sense I built it upstream rather than around it. Seven of those pull requests were merged:

Testing GraphQL with PHPUnit

The team had struggled to test the API, mostly because GraphQL is non-trivial to exercise with Laravel's test client out of the box, and because single responsibility problems in the core classes that talked to the blockchain made mocking difficult. I made it my mission to make it easy for developers on the team to write tests for their changes.

The steps I went through to get there were:

  • Moving Ethereum hexing out of traits into dedicated support classes, so that classes could be mocked without breaking string manipulation.

  • Separating networking logic into client classes, so that we could easily mock them with simple fakes or Guzzler.

  • Extending the base TestCase and TestResponse classes with helpers that make it easy to perform GraphQL queries and mutations, and assert on the responses.

  • Simplifying the authentication strategies, and breaking the single schema up into several, so that resolvers had fewer access control responsibilities.

  • Stripping unrelated functionality, and fanning overcomplicated mutations out into several that better communicated the intent of the user.

The suite grew to nearly 900 tests, around 500 of them mine, and made it a lot easier to establish what was possible with the existing APIs, and to design what the new schema should look like in v2.

PHPUnit Test Run
The PHPUnit suite

Next steps with the v2 API

Enjin's API is unusual in that it has two primary consumers: game developers, through their server backends, and players, through a client-side game. Many games want to do everything server-side, and so need a broad set of access controls, but when they generate an access token for a player and pass it down to the client, those players need very limited abilities.

Many challenges in the v1 API came from queries and mutations trying to serve a broad range of user types at once, which created lots of noise and hid what each resolver's core business logic really was.

For the v2 API I proposed splitting it into two distinct schemas, app and player, to remove this ambiguity. The schema would make it clear which queries were available to whom and how to authenticate for them, while letting us drop lots of deprecated behavior. I prototyped it before I moved on.

Migrating from Vue Material to Vuetify

The frontend was originally written in Vue Material, but that project seemed somewhat stagnant, and we needed to make fairly significant changes to our implementation anyway, so I decided it was time to move to Vuetify, to use its more established components, such as data table pagination. Tables, forms, sign-in and the toolbar came first.

Some of the other significant frontend work I led included:

  • Switching to a private API, so that we could use session auth, and keep the frontend's concerns apart from the public API's backward compatibility.

  • Moving from access token to session auth, to standardize the way the app worked with the rest of the Laravel suite, and remove some bespoke behaviors from the auth system.

  • Standardizing interfaces under "page components" that wrapped up common theming, loading spinners, calls to action, and general layout.

  • Significant improvements to the mobile UI, embracing material design, and making sure data tables collapsed in a way that was both attractive and useful.

  • Laravel Nova for admins, and the upgrade to Laravel 6.

The result was a significant improvement for both desktop and mobile users, and made it a lot easier for other developers to add pages that conformed to the application's style guide.

Enjin Cloud Mobile UI
Enjin Cloud on mobile

References

Witek Radomski, Enjin CTO

Steve is an experienced, high-performing developer. He was able to laser-focus on certain architectural issues in our v1 codebase and was not afraid of the difficult work needed to do a refactor. We brought him onto our team to improve our complex blockchain API codebase that depended on a lot of different separate applications. He was quickly able to understand the entire project's architecture and improve what we had. Steve was diligent with code reviews & pull requests and constantly encouraged us to improve our own internal processes. His commits were frequent and he was able to resolve a ton of bugs and lay the groundwork for our v2 backend. He definitely doesn't mince words and will tell you outright if there's a better way to do things. I would highly recommend working with Steve!

Witek Radomski, Enjin CTO