How to Use GraphQL Schema Language

The GraphQL schema language is a human-readable syntax to help create GraphQL schemas. In this lesson, we'll use the GraphQL schema language to re-create the schema we have in a more readable and simpler format.

Project Source Code

Get the project source code below, and follow along with the lesson material.

Download Project Source Code

To set up the project on your local machine, please follow the directions provided in the README.md file. If you run into any issues with running the project source code, then feel free to reach out to the author in the course's Discord channel.

Table of Contents

This lesson preview is part of the TinyHouse: A Fullstack React Masterclass with TypeScript and GraphQL course and can be unlocked immediately with a single-time purchase. Already have access to this course? Log in here.

This video is available to students only
Unlock This Course

Get unlimited access to TinyHouse: A Fullstack React Masterclass with TypeScript and GraphQL with a single-time purchase.

Thumbnail for the \newline course TinyHouse: A Fullstack React Masterclass with TypeScript and GraphQL
  • [00:00 - 00:12] Apollo already comes with all the tools we need to define a schema using the GraphQL schema language. The GraphQL schema language uses a very simple syntax to define and create a GraphQL schema and its language agnostic.

    [00:13 - 00:27] Most GraphQL server libraries provide us with the capability of creating a schema with the GraphQL schema language and in fact, the GraphQL JavaScript library allows us to do this as well. But we'll achieve this with the Apollo Server Express package.

    [00:28 - 00:41] Apollo Server allows us to define a schema by setting up two different values. The type definitions, which is a string representing a GraphQL schema and the resolvers, which is a map of functions that implement the schema.

    [00:42 - 01:00] We'll create these in separate files located within a GraphQL folder that's kept within the source folder. We'll also create an index.ts file in this GraphQL folder to gather the type definitions and resolvers map and export them explicitly from the GraphQL folder.

    [01:01 - 01:22] We'll begin by creating the schema definition in the type definitions file. We'll create the GraphQL schema using the GraphQL schema language and to do so, we'll import and use the GQL template literal tag from the Apollo Server Express package.

    [01:23 - 01:48] The GQL tag will allow us to write GraphQL in our code by having strings be parsed as a GraphQL abstract syntax tree. Let's see this in action. We'll export and create a constant variable named type-deafs, type-definitions, that has the GQL tag wrapped around a template literal string, or ES6-backed tags.

    [01:49 - 02:04] We'll first define the listing object type like we've done before, but in this instance we'll use the syntax that the GraphQL schema language gives us. We can define a new object type by using the type keyword and then specify the name of the type, listing in our case.

    [02:05 - 02:29] We can then declare the fields of our listing object type and use the built-in scalar types to reference the types of each field. ID for the ID type, string for the string types, int for the integer types, recall how we've wanted each of the fields within the listing type to never be null.

    [02:30 - 02:43] In the previous way, we needed to wrap our type definition with the GraphQL non -null type. With the GraphQL schema language, we can simply place the exclamation point after our type definition to declare that this type should not be null.

    [02:44 - 02:56] With the listing type defined, we can go ahead and declare the shape of the root query and mutation types. We intend on having a single listings field be in our query that's responsible in returning a list of listing object types.

    [02:57 - 03:11] With the GraphQL schema language, we can simply wrap that type with square brackets to denote this. For our listings query field, we want to ensure the value returned is not null and it contains a list of not null values.

    [03:12 - 03:27] So we'll place the exclamation marks accordingly to denote this. And finally, we'll declare the shape of the mutation object type that contains a delete listing field and must return a listing and not a null value.

    [03:28 - 03:39] The delete listing field is unique since it's the only field we have that accepts an argument. We can specify the ID argument it expects here and declare a edit is of type ID .

    [03:40 - 03:56] The GQL tag helps parse the string specified into a GraphQL abstract syntax tree and a Apollo server requires us to use it to wrap our schema. Now for clarification, GQL here is a function that takes a string as an argument.

    [03:57 - 04:07] In addition, the string has to be constructed with template literals. You might be wondering why this function appears a little strange since it's literally a template string beside the GQL reference.

    [04:08 - 04:22] This is a particular ES6 feature known as tagged template literals, which isn't commonly used. The takeaway here is that GQL is a tag, or data to say a function, where the argument is derived from the template literal applied alongside it.

    [04:23 - 04:30] It takes the string and essentially returns a GraphQL tree. Now what are some of the benefits this GQL tag brings us?

    [04:31 - 04:47] By using the GQL tag, it helps us easily manipulate the GraphQL document, by making it easier to add, remove fields and perform more complicated functionality like merging queries. This is most apparent when we install and use an accompanying editor extension.

    [04:48 - 05:05] So we'll go ahead and install VS Code's Apollo GraphQL extension. And once installed, we'll actually get appropriate syntax highlighting for all our GraphQL documents created with the GQL tag.

    [05:06 - 05:20] We'll now move towards creating the resolvers of our GraphQL schema, with which we'll do in the resolvers file. Here is where we'll provide the functionality to all our schema entry points for fetching and manipulating data.

    [05:21 - 05:34] In other words, here is where we define our GraphQL resolvers. We'll import the listings data array since we'll need it in our resolver functions, and we'll export a constant object called resolvers.

    [05:35 - 05:46] The resolvers object is simply going to be a map that relates the schema fields to the functions that resolve that field. First, we'll define the query root object and the listings field resolver.

    [05:47 - 06:00] The listings field resolver will simply return the listings array from our mock data. We'll declare the mutation object type with the delete listing field resolver.

    [06:01 - 06:21] The delete listing resolver expects an ID argument that we're going to need to access. Now arguments are positioned as the second argument of resolver, so we'll declare the root object argument first, prefix it with underscore since we won't use it, and we'll destruct the ID field from the arguments parameter.

    [06:22 - 06:35] The delete functionality would appear just like before, where we splice the appropriate listing object within the list. So we'll copy the functionality that we already have from our old GraphQL file, and simply paste it as is in here.

    [06:36 - 06:49] We get a warning in our argument section of our resolver, since with the Apollo Server library, these types aren't explicitly defined as any, configuration. so they're actually recognized to implicitly have the any type.

    [06:50 - 07:15] The first object argument essentially contains the result returned from a res olver on a parent field. We haven't configured anything here to be defined, so roots in this particular context will be undefined, and as a result we can give it the undefined type.

    [07:16 - 07:32] ID in the GraphQL schema is of type ID. The ID GraphQL type essentially gets serialized as a string when the function is run, so we'll type to find the destructured arguments object here, and give the ID property the string type.

    [07:33 - 07:48] Apollo Server provides some interface types to help better define the types of a resolver map. We'll import the iResolver's interface and type to find our resolver's map with it.

    [07:49 - 08:14] Now, if we try to introduce a new key into our resolver's map, that doesn't have an object resolver fields or isn't a resolver function field itself, as an example, if we just gave it a string value, TypeScript will complain. The iResolver's interface is a generic and actually allows us to abstract the types of the object and context arguments in our resolver functions.

    [08:15 - 08:32] We could, if we wanted to, specify undefined as the first type argument in our iResolver's interface, and this will type to find the object arguments in all our resolver functions. We prefer doing it explicitly in each of our resolver functions, so we won't abstract these types.

    [08:33 - 08:41] And in addition, we're going to spend more time in the upcoming lessons explaining what TypeScript generics are. And that's it.

    [08:42 - 09:00] In the index.ts file within our GraphQL folder, we'll export the resolvers and type definitions objects from their respective files. And in the index.ts file of our source folder, we'll import the type definitions and resolvers directly from the GraphQL folder.

    [09:01 - 09:19] We can now modify our Apollo server instantiation to take the new method of defining the schema by accepting the type definitions and resolver's options. We'll also remove the standalone GraphQL file we had before, where we instant iated our schema with the help of the GraphQL JS library.

    [09:20 - 10:01] We'll head over to the terminal again, start our server, and with our server running, we'll head back to the browser, refresh our GraphQL Playground, and our listings query and delete listing mutations should work as expected. You may have noticed that whenever our server app restarts, all the listings we deleted previously comes back.

    [10:02 - 10:13] Let's try it right now. We're going to exit the server, start it again, and when it's running, we're going to head back and attempt to query all the listings.

    [10:14 - 10:25] We get the original array back, regardless of how many times we've deleted the listings before we exited the server. This is because we've hard coded data into our app in the listings.ts file.

    [10:26 - 10:36] So whenever our server restarts, the code in listings.ts is reloaded into our computer memory. For a real application, this makes our server pretty useless.

    [10:37 - 10:47] If we want our actions to be permanent, we must somehow persist our modified data outside of our app. Databases are designed to do just this.

    [10:48 - 11:13] [ Silence ]