If you had the pleasure of joining us last time, we had just completed a crash course in structuring GraphQL Queries. As much as we all love studying abstract queries within the confines of a playground environment, the only real way to learn anything to overzealously attempt to build something way out of our skill level. Thus, we're going to shift gears and actually make something with all the dry technical knowledge we've accumulated so far. Hooray!
Data Gone Wild: Exposing Your GraphQL Endpoint
If you're following along with Prisma as your GraphQL service, the endpoint for your API defaults to [your_ip_address]:4466
. What's more, you've probably noticed it is publicly accessible. THIS IS VERY BAD. Your server has full read/write access to whichever database you configured with it... if anybody finds your endpoint hanging out in a Github commit somewhere, you've just lost your database and everything in it. You're pretty much Equifax, and you should feel bad.
Prisma has a straightforward solution. While SSHed into wherever-you-set-up-your-server, check out the prisma.yaml file generated when we first started getting set up. You know, this directory:
prisma.yaml seems inglorious, but that's because it's hiding a secret... or should I say, it's not hiding a secret! Hah!... (you know, like, credentials). Anyway.
To enable authorization on our endpoint, we need to add a secret to our prisma.yaml file. The secret can be anything you like; this is simply a string used to generate a token. Add a line that defines secret
like this:
With your secret stashed away safely, the Prisma CLI can now use this secret to create the authentication token. This will be the value we pass in the headers of our requests to interact with our Prisma server remotely.
Type $ prisma token
in your project directory to get the work of art:
Nice; believe it or not, that was the "hard" part.
EXTRA CREDIT: Assign a DNS Record and Apply a Security Certificate
If you really want to, you could already query against your insecure IP address and start receiving some information. That said, making HTTP requests as such from HTTPS origins will fail. Not only that but you kind of look shitty for not even bothering to name your API, much less apply for a free SSL certificate. For the easiest way to do this, see our post on using Caddy as an HTTP server.
Building a Javascript Client to Consume Our API
With our API nice and secure, we can start hitting this baby from wherever we want... as long as it's a Node app. We'll start by requiring two packages:
const { GraphQLClient } = require('graphql-request')
const { dotenv } = require('dotenv').config()
GraphQLClient
is the magic behind our client- it's everything. It is also very similar to existing npm libraries for making requests, such as node-fetch.
We'll also leverage the dotenv
library to ensure our API endpoint and Bearer token stay out of source code. Try not to be Equifax whenever possible. dotenv
allows us to load sensitive values from a .env
file. Just in case you need a refresher, that file should look like this:
NODE_ENV=Production
ENDPOINT=https://yourapiendpoint.com
AUTH=Bearer eyJhbGciOBLAHBLAHBLAHBLAHBLAHBLAHBLAHBLAHGUYFIERIBLAHBLAHBLAHBLAHBLAHBLAHBLAHBLAHBLAHBLAHBLAHBLAHBLAHBLAHBLAHBLAHZl-UGnMrOk3w
Initialize The GraphQL Client
I like to set up a one-time client for our API that we can reuse if needed. After pulling the API endpoint and token from our .env
file, setting up the client is easy:
EMERGENCY MEETING: EVERYBODY HUDDLE UP
I'm sorry, were you focusing on development? Unfortunately for you, I spent 8 years as a product manager, and I love stopping everything suddenly to call emergency meetings.
Real talk, though; let's think back to the JIRA Kanban board example we've been using for the last two posts. If you recall, we will write a query that populates a 4-column Kanban board. The board represents a project (in this case, Hackers and Slackers) and each column represents the status of a ticket, like this:
We've previously established that GraphQL queries are friendly to drop-in variables. Let's use this to build some logic into our client instead of hardcoding a massive query, which is really just the same 4 queries stitched together. Here's what a query to populate a single JIRA column looks like:
We're passing both the project and the issue status as variables to our query. We can make things a bit dynamic here by looping through our statuses and executing this query four times: each time resulting in a callback filling the appropriate columns with JIRA issues.
Game On: Our Client in Action
const { GraphQLClient } = require('graphql-request')
const { dotenv } = require('dotenv').config()
const endpoint = process.env.ENDPOINT;
const token = process.env.AUTH;
// Initialize GraphQL Client
const client = new GraphQLClient(endpoint, {
headers: {
Authorization: token
}
});
// Structured query
const query = `
query JiraIssuesByStatus($project: String, $status: String) {
jiraIssues(where: {project: $project, status: $status}, orderBy: timestamp_DESC, first: 6) {
key
summary
epic
status
project
priority
issuetype
timestamp
}
}
`;
// All Possible Issue Statuses
const statuses = ['Backlog', 'To Do', 'In Progress', 'Done'];
// Execute a query per issue status
for(var i = 0; i < statuses.length; i++){
var variables = {
project: "Hackers and Slackers",
status: statuses[i]
}
client.request(query, variables).then((data) => {
console.log(data)
}).catch(err => {
console.log(err.response.errors) // GraphQL response errors
console.log(err.response.data) // Response data if available
});
}
Works like a charm. We only had one endpoint, only had to set one header, and didn't spend time reading through hundreds of pages of documentation to figure out which combination of REST API endpoint, parameters, and methods actually got us what we wanted. It's almost as if we're writing SQL now, except... it looks a lot more like... NoSQL. Thanks for the inspiration, MongoDB! Hope that whole selling-open-source-software thing works out.
Oh, and of course, here were the results of my query:
Before we say "GG, 2ez, 1v1 me," know that we're only getting started uncovering what GraphQL can do. It's not all just creating and deleting records either; we're talking full-on database JOIN
equivalent type shit here. Stick around, folks; the bandwagon's just getting warmed up.