Skip to main content
whitep4nth3r logo

TIL: How to use GraphQL variables to give my queries type safety

How can you make sure your GraphQL queries are safe from nasties? Let’s find out.

⚠️ This post is over two years old and may contain some outdated technical information. Please proceed with caution!

One of the things I love about GraphQL is how straightforward it is to get up and running with little to no experience. Using a browser-based GraphiQL interface — such as the GraphiQL explorer provided by Contentful — you can inspect your schema right there in the browser, and construct your queries in no time. But how can you make sure your GraphQL queries are safe from nasties? Let’s find out.

To inspect your schema in Contentful’s GraphiQL interface and construct a GraphQL query, enter this URL in your browser, and swap out the SPACE_ID for your Contentful space ID, and ACCESS_TOKEN for your Contentful Delivery API Key.

https://graphql.contentful.com/content/v1/spaces/{SPACE_ID}/explore?access_token={ACCESS_TOKEN}

Make an HTTP POST request with your programming language of choice — and boom — you’ve got data.

This is an example of a query that we can use to request data from a single blog post by slug. Notice how we’re using a where clause to filter the items by a slug that matches a string we supply.

{
blogPostCollection(where: {slug: "what-is-a-rest-api"}) {
items {
slug
title
excerpt
readingTime
}
}
}

And here’s the data we get back.

A screenshot of the GraphiQL explorer showing a query made using a string in the where clause, and the data successfully returned.

Here’s how we can make the same request using JavaScript fetch (and no external dependencies!).

const query = `{
blogPostCollection(where: {slug: "what-is-a-rest-api"}) {
items {
slug
title
excerpt
readingTime
}
}
}
`
;

const response = await fetch(
`https://graphql.contentful.com/content/v1/spaces/${SPACE_ID}/environments/master`,
{
method: "POST",
headers: {
Authorization: `Bearer %ACCESS_TOKEN%`,
"content-type": "application/json",
},
body: JSON.stringify({ query }),
},
).then((response) => response.json());

console.log(response);

All of this is great, and perfectly valid GraphQL. And, if you’re using a static site generator such as Next.js, Gatsby or Nuxt which will pre-render your pages at build-time and serve static pages to the client, you should be good to go. I’d been doing this for months using Contentful’s GraphQL API to fetch my data to power my personal website built with Next.js

However, whilst queries like this are super-fast to write and get your projects out fast — what if you’re making GraphQL queries dynamically on the client and not as part of a static site build? What if someone could play around with your data in real-time by inserting an incorrect data type, a GraphQL mutation or similar instead of a string? 

Here’s where GraphQL variables save the day!

It’s worth mentioning that because the Contentful GraphQL API is read-only, this kind of scenario won’t happen — but security considerations are always good to bear in mind regardless. Let’s take a look!

Use GraphQL variables for type safety and self-documenting queries

GraphQL variables offer an extra layer of protection in your queries, namely type safety — meaning that a query will only accept dynamic variables of certain data types, such as String, Int (number), DateTime and so on. And what’s more, there isn’t much more work needed to make your GraphQL queries safer!

To use variables in your GraphQL queries:

  1. Create what looks like a traditional function prefaced by the word query. You can name this query function whatever you like. I’ve named mine GetBlogPostBySlug.

  2. Inside the parentheses of the function declaration, name and define the types of the variables the query will accept, and prefix your variable names with a $. The query below will accept a variable named $slug, which is of type String. The bang or exclamation mark that comes after the type name means that it is a required variable for the query to execute.

  3. In an HTTP POST request to the GraphQL API, variables are passed to the GraphQL request as a separate property inside the body of the request. Click on the query variables pane at the bottom of the GraphiQL explorer. Create an object, and add your stringified variable name and value as “key”: “value” (it’s important to stringify the key name here!).

A screenshot of the GraphiQL explorer showing the slug variable used correctly, passed in via a separate query variables tab, and the data returned is the same as the previous query.

Let’s look at an example of using GraphQL variables using JavaScript fetch. Notice how we have replaced the original query with the function-style query from above, and have created a variable named variables that we pass into the body of the HTTP request.

const query = `query GetBlogPostBySlug($slug: String!) {
blogPostCollection(where: {slug: $slug}) {
items {
slug
title
excerpt
readingTime
}
}
}
`
;

const variables = { slug: "what-is-a-rest-api" };

const response = await fetch(
`https://graphql.contentful.com/content/v1/spaces/${SPACE_ID}/environments/master`,
{
method: "POST",
headers: {
Authorization: `Bearer %ACCESS_TOKEN%`,
"content-type": "application/json",
},
body: JSON.stringify({ query, variables }),
},
).then((response) => response.json());

console.log(response);

And that’s how I learned to make my GraphQL queries type-safe and free from nasty attacks on dynamic API calls! 

Going further with more types

There are a variety of different variable data types available on Contentful’s GraphQL API. As well as the standard data types such as String, Int and DateTime, you can also pass variables to a query that are entry-specific and API-specific.

To inspect the types available on your schema, click on the Docs links at the top right of the GraphiQL explorer:

A screenshot of the GraphiQL explorer, showing the docs button a the top right highlighted.

Click on Query:

A screenshot of the GraphiQL explorer, showing the docs tab open and the root types — query area highlighted.

And find the content type you’d like to inspect.

A screenshot of the GraphiQL explorer, showing the query tab open to inspect the scheme, with the type order: EventOrder highlighted.

Another thing I learned on this journey is that you can’t use variables in GraphQL for everything — namely keys in WHERE clauses.

I recently created a GraphQL query to fetch the events on my website (note: this is no longer relevant on my current website!). On the main events page, I wanted to show future events in ascending order, and on the past events page, events in descending order.

The two supported variables involved in this query are:

  • $order — date_ASC or date_DESC

  • $date — as an ISO string

But I also needed a third dynamic variable — which was to control whether the API returned events before (date_lt — date less than) or after (date_gt — date greater than) a particular date. Unfortunately, this part of a GraphQL query cannot be controlled with a variable, and so I had to be creative and pass in a calculated string to the query like so:

// https://github.com/whitep4nth3r/p4nth3rblog/blob/main/contentful/Events.js

import ContentfulApi from "@contentful/Api";

const defaultOptions = {
future: true,
};

/*
* Get all events -- future by default
*/

static async getEvents(options = defaultOptions) {
// Calculate date_ASC for future events, or date_DESC for past events
const order = options.future ? "date_ASC" : "date_DESC";

// Generate today's date
const date = new Date();

// And format it to an ISO String
const formattedDate = date.toISOString();

// Decide on the date filter to pass in as a string
const dateFilter = options.future ? "date_gt" : "date_lt";

// Construct variables object to send with the HTTP POST request
const variables = { date: formattedDate, order };

// Build the query
const query = `query GetEvents($date: DateTime!, $order: [EventOrder]!) {
eventCollection(where: {
${dateFilter}: $date}, order: $order) {
items {
sys {
id
}
date
name
link
description
timeTbc
isVirtual
image {
url
description
height
width
}
}
}
}
`
;

// Call out to the base API call
const response = await this.callContentful(query, variables);

const eventCollection = response.data.eventCollection.items
? response.data.eventCollection.items
: [];

return eventCollection;
}

One other thing to notice is that the $order variable is of type EventOrder, which we saw when we inspected the schema above, which is an API and entry-specific type!

So there you have it. Fancy and safe GraphQL queries, so you can build great stuff with the Contentful GraphQL API without worrying. You can check out the code on GitHub for the full range of queries I make with GraphQL on my website, and if you’re curious about GraphQL and want to learn more, you can learn along with Stefan Judis’ React and GraphQL video course in our developer portal. Happy querying, friends!

Originally posted on www.contentful.com

Like weird newsletters?

Join 337+ subscribers in the Weird Wide Web Hole to find no answers to questions you didn't know you had.

Subscribe

Salma is looking at you, with a rather large smile. She's pointing across herself up to her left, with a very tatooed arm. She's wearing a black shirt and black rimmed glasses.

Salma Alam-Naylor

I'm a live streamer, software engineer, and developer educator. I help developers build cool stuff with blog posts, videos, live coding and open source projects.

Related posts

13 Dec 2021

An introduction to GraphQL and how to use GraphQL APIs

Here's everything I learned this year about GraphQL for anyone getting started.

GraphQL 10 min read →

14 Jun 2021

How to filter entries by linked references in GraphQL

Check out this quick guide that shows you how to get the data you need using the linkedFrom field in your query.

Tutorials 4 min read →