 Hey, folks. Welcome to TypeSafe GraphQL with TypeScript. My name's Aaron Powell, and I'm a Principal Cloud Advocate at Microsoft. Over the last 15 or so years, I've been doing web development, starting with languages like ClassKSP and PHP, through to .NET, compiled languages on the server, and then eventually to JavaScript on the server and JavaScript in the client. In my time, I've been building web applications. I've seen all sorts of different ways we can do data access, whether it's just normal page refreshers and getting more data on request to custom-built APIs to serve out, you know, kind of like a AGAC sort of endpoints for an application through to really strict rest implementations. But today, I want to talk about specifically GraphQL, which is another way which we can do data access. If you want to get in touch with me after the session, you'll find all my details here on the slides, and do feel free to reach out if you've got any questions that I don't get a chance to cover off in the Q&A, I'll do my best to answer them after the session. Or at least help you along the way of finding the answers if I don't know them myself. But that's enough about me. You're here not to learn about me in my background, my history, you're here to learn about GraphQL and TypeScript and how we can work with those together. But before we get into looking at how we can build an application, I want to talk a bit about what GraphQL is and why it's interesting. GraphQL is originally created by Facebook as a query language for doing client and server interactions. It's not how you implement a server or it's not a client implementation, but it's more a specification of how those two can talk to each other. So you still build a server implementation, whether it's in JavaScript, .NET, Node, Python, etc. We can build whatever we need for the kinds of applications we're building, and then we can build a client that's also going to be able to talk over the same protocol, generate the kinds of queries that is expected and send them to the server. So what makes GraphQL different to REST? REST is the most common way that people have built APIs in at least in the past. Well, the main difference is that REST is very centered around the way the server visualizes its data and wants to expose that out. Let's say we're building a trivia application, and inside of that, we're going to have three primary model types. We've got our game, we've got players, and we've got questions. Now in a REST scenario, at least in a very strict approach to doing REST, probably have a game endpoint, so slash API slash game, and then from that, we can get back a game based off its ID, and then from that, we would have an array of IDs of questions and players that we then have to follow through and then do additional requests to get all that information. Now this might be a little bit over complex for some scenarios where you are fetching data, but it's also going to be ideal for others. I'm not here to tell you that you should be using GraphQL or REST for the current applications. You've got to work out that one on your own, depending on the kinds of applications you're building. GraphQL though is kind of the flip model for compared to REST. So where REST is all about what the server defines how you do your data access, GraphQL is more about how the client wants to be able to get data. You still expose the data models that your server has available to it, but it is up to the client to determine how it wants to query for that data, and then to build out the queries that are going to be right for the particular requests that it's needs to be doing and for the data it needs at that point in time. Now this can avoid scenarios where you're over fetching data, getting data that you don't actually need at that point in time because you're trying to say, just display the high score board at the end of a trivia game. Well, maybe you don't need all of the questions, but you unfortunately might have to get those if depending on how the REST model is set up. In GraphQL, you can be a lot more selective about that and only get the data that you need. But as I said, I'm not here to tell you exactly when to use REST versus GraphQL. I'm here to look at the specific scenarios of how we can do type safety with GraphQL. So why is type safety with GraphQL a topic of conversation? Well, with the GraphQL schema that we can build out, it's going to be type safe. We define strongly typed data models and the fields that are available with inside of those data models. We then define strongly typed queries and mutations so how we can get data and how we can change data that are available with inside of our server and for our clients to communicate on. So having this type information originally to start with is a good building block for building a type safe application. But with JavaScript, well, we can lose a lot of that type safety anyway because JavaScript is an inherently untyped language. That's where TypeScript can come to play. So what are we going to be building today? Well, we're going to be building an application with two components. We're going to start with the server and for that server, I'm going to be using the Apollo GraphQL framework. So this is a server implementation of GraphQL that can run in a variety of different manners whether it's on a web server platform like Express or Coa or you can run it in a serverless model which I'm going to be using as your functions to do. I just like the serverless model but it's the same approach to whether you're doing a server or a serverless model or whether you're using any of the different underlying providers that Apollo supports. And then from a client standpoint, well, we're going to need a client that's going to connect to that and I'm going to be building a React based web application. Now the stuff that we're covering up here isn't exclusively supported by React. It could be applied to any of the other JavaScript frameworks you might be using whether it's Felt, Angular, View, et cetera. I just prefer React because it's the one that I've had the most experience with and it's kind of my go-to approach with doing web applications. But like I said, it's entirely up to you how you want to do the client and the stuff I will be showing you is applicable to pretty much every JavaScript framework. But as I said, we want to do end-to-end type safety. So that's where TypeScript is going to come into play. I'm going to be using TypeScript as a way to model out our backend and also do strongly typed front-ends. But what I want is the type information I have in my GraphQL schema available on both client and server. So they are literally talking the same language. So let's jump out of the slides and have a look at how we can do that with a bit of a demo. I'm going to go through a subset of a larger workshop that I've got, I'll show the link to that at the end if you're interested. And we're going to be building out a couple of different pieces that look at the specific type safe aspects of building a GraphQL server. So here we are in VS Code and it's time to get started. So as I said, this is a workshop where I've already got some of the stuff set up and here is a GraphQL schema that I've predefined. As we can see, I've got a number of types that are available. I have a question type, a game, that game has questions available to it. So we've got types that reference other types that we have. I have some enums. And if we scroll down a little bit further, we'll see that we have a query. So this is the entry point when someone is doing a request against a GraphQL server and these are the things that you're able to do against it. You're able to get back a game by its ID. You're able to get back all the games in the system and you're able to get back a player's results for a particular game. So there are answers so we can work out how many they got correct and versus how many they got incorrect. What we're going to need to do is we're going to need to implement some resolvers that work with these. So resolvers are functions that handle the queries or handle the GraphQL objects as they're being parsed through. So let's pop up into another file called resolvers.ts. I'm going to put the schema on the side here. So inside of resolvers, I'm going to start building out those queries that I've got here on the side and functions that handle those. So in my resolver I have query which is a type name that matches the same type that had inside of my schema. And let's start with the easiest one, which is game. So sorry, games, because we just want to get all of the games and well, I need to return something. But this is just an object that I've created. It doesn't understand any type information. It's got no correlation to the type of information that we have with inside of our schema. But because I've worked with Apollo before, I know that the first argument is going to be the parent of this in the object graph, but this is a root entry point for a GraphQL schema. So I don't have a parent at this point. Any arguments that can be provided to this operation, I don't have any arguments at this point in time because games doesn't take any arguments. And then finally I have a context where I can provide something, or I can get access to like services that have been injected and other stuff that are available for the lifetime of this request. One of those things is data sources and that's going to give me access to my underlying database. I know that that's there because I've implemented this application and I know the code base, so I know what's there, but I don't have any type information. I don't know that if I do data sources that there are any fields off of this I could access. Having worked on this code base, I know that I can do dot game, which is a property available there and I can get games off that. And that's going to return something that can be handled by GraphQL. Similarly, if I want to implement the games, the games resolver, so I do game, and then again, we don't have a parent. I know that the second argument will have an object which is the arguments that are provided by the query and I can decompose this with the ID. And finally I can get data sources again. And I know that I can return data sources, game got get game and provide it with the ID. But again, I only know this information because I built the code base myself. If we have a look at any of this, it's all just untyped and I have my NEs everywhere and well now TypeScript really isn't giving me any value. So I need to work out how can we get some of that schema value, schema typed information here to make TypeScript giving us some actual value or I just throw away TypeScript. Those are kind of the two endpoints that I've got here. We'll jump through to a slightly more completed version of the application and have a look at that. Let's open up the resolvers again and we'll see that I've gone through and well I've got the same resolvers for game and games. I also have my player results. So this is how I can get back the results that a player has submitted. So they're answers. We'll see that again, we're decomposing a couple of fields out of there. And without having the schema side by side, do I know that it's game ID and player ID? I'm gonna assume they are because that's what I wrote previously in the demo before I started. But yeah, if you didn't have that schema like immediately available, it's a little bit opaque, we don't know what's there. Well, so this is an async function. So we're awaiting on the get game method on our game data source. But did we realize that that was something that returned a promise by looking at it previously? No, we didn't. We know that because we know this code base and we can dig through it. So this is where we wanna start looking at how we can get that TypeScript information. I'm going to install a new package, install, there it is. I called GraphQL Code Gen slash CLI. So GraphQL Code Gen is a series of packages for generating code from GraphQL. And this is a command line tool that will then allow me to generate out the TypeScript definitions that I need. So let's run NPX GraphQL Code Gen in it. And it's gonna prompt us through a bit of a wizard on what we want. We wanna build a backend or API server. The schema is at slash GraphQL slash schema dot GraphQL. GraphQL, there we go. We wanna generate the TypeScript model definitions. And we also want to generate the TypeScript resolver definitions. So we can strongly type our resolvers. We wanna output this to say Graph dot slash GraphQL slash generator dot TS. We generate an introspection file. So that's just some debugging information. We'll leave the default YAML file name and we're gonna add a NPM run script called Gen. That will be so that we can just run the code generator without having to do NPX every single time. So this is now gonna go ahead and it's gonna create us our YAML file for our config. It's going to then update our package JSON with that new run script and with some additional dependencies that we're going to need and basically get our environment ready so that we can go ahead and build our GraphQL, our strongly typed information from our GraphQL schema file. All right, so if we do NPM run Gen, we'll see that that's gone ahead and done it. Oh, and it looks like I have got an error in something that I've added and I forgot to install my dependencies. Oops, NPM install, because it added some new things to my package JSON and I completely forgot to do an NPM install to make sure that they are installed as well. So that's just the joys of live demo is you've gotta remember all the steps that you meant to run. NPM run Gen, we'll do that again. Take two. And excellent, that's worked successfully. Let's close off our terminal, have a look and here is our new generated file. So this has got a whole bunch of TypeScript type definitions. We'll see that we've got question here and if we were to pop open our schema on the side, go back to generated, we'll see that that's all the same fields. We have an ID, which is an ID type which is using the scalar type of ID. We'll see that we've got our question, correct answer and answers which are strings or arrays of strings. We'll see a bit further down, we have game which has a scalar types and then it's got some optional types. So the game state is an optional value or we've got arrays which have got player and question, so on and so forth. You kind of get the idea of where I'm going here. One to one mapping between what our schema had. What that does mean is that I can come into my resolver file here and then I can say that I want to strongly type this as resolvers, resolvers, resolvers, there we go. And that's going to come from API GraphQL Generate. So this will import that in and now we'll see that say the ID is strongly typed as string. Fantastic, cause we know that that is off our schema coming in as a string ID. We know that that's correct, excellent. So we can, we know that that's there. We know that same with player ID is going to be all correct. We know that the player result needs to return a particular type and so on and so forth. So we're getting some of that type safe information. We still don't know what our data source is though, which is a little bit of a problem because well, is it game or is it games? I'd have to dig through my own code base to remember. It's the kind of thing that you don't hit to runtime. But let's just do an NPM start, kick off our API and have a look at what's actually up and running. Now we'll give this a moment. It's going to compile our TypeScript and there we go, that's all up and running. We'll start our local web server. Here we go. Here's our GraphQL endpoint and we'll zoom that in once it loads, excellent. This is loading up the GraphQL playground and we'll see that excellent. We can perform a query and here's a query that I've previously done. So it's just trying to dump out all of the games, the state of the game, its ID and the questions that were asked. So if you get wrong on this, it turns out that we get another error. You'd think that even if you were doing a prerecorded talk you wouldn't have runtime errors and stuff like that. You should get those in anything. But no, this is actually an error that I expect. What it's saying is it can't return null from a non-null field, question.correctAnswer. Well, that's because what we're doing is, well, actually, let's just jump back to VSCode and we'll have a look at that. Close off our terminal and we'll see that in our schema, if we go up to the top, we have correctAnswer. But if we were to go through to data slash type, so this is the definition of our database. So this is our databases, the way our database visualizes types. Well, we'll see that we actually store correct underscore answer, not correct answer as Pascal case. And similarly, we have incorrect answers, not answers in rate. So the way that the schema is exposing stuff doesn't 100% map to what our database is because we don't want to expose our database directly. We are doing a bit of translation. So we need to do something that tells us, well, these two models need to map back and forth between each other as well, something that we're currently not doing. And for that, we can add some model mapping inside of our code gen file. So let's open up our code gen at YAML and we're going to add some model mappers. Actually, we'll jump across and I'll show this in a completed version rather than potentially doing some more runtime errors. We're running out of time for today. So let's try and get things working as much as we can. Let's come into API, code gen at YAML. And if we have a look here, I've configured my code generator. So it's got some context, come back to that in a moment. Most importantly, we have mappers. This mapper is saying, when you find a GraphQL type of question, we're going to use our question model type, which is defined in TypeScript. If we do npm run gen here, npm, oh, I am in the wrong folder. Where are we? Where are we going? We are going to completed API. There we go. And npm run gen. So we'll just generate out our TypeScript file. Excellent. That'll be ready in just a minute. There we go. And if we have a GraphQL generated, now we have a bit of additional information that's mapping between our questions and our players. Because what it's doing is it's detecting that whenever it finds a question, it's going to return a question model. So how does this work with inside of our resolver? Well, now our resolver function is going to return a type of, this is returning a game model, which is expected here. Or player results is going to return a different kind of model. And we're then going to map those a bit further down with our object mappers with inside of our resolver. So here it's saying, if you find a question, so this is a type that you need to return from GraphQL, we're going to receive a question model. So that's its parent this time. And then from that, we're going to generate a new thing which is going to return an array of strings. So we're going to take the incorrect answers, the correct answer and then return that out. Similarly with our correct answer, we're just going to return the correct underscore answer field from the question model. So now if I change my underlying data structure, so inside of my database is only called correct answer, I change it to correct answer with the Pascal case. Well, then this will start being a compilation error because this field no longer exists. And then I know that I can remove this bit of code because I know I'm gonna need to do that custom mapping. So this type end-to-end type safety is going all the way from our underlying database through to what our schema has defined. And then we're having our resolver, the thing that works to kind of merge those two together, understand that type safety. The other thing that I'm doing here is, well, I want my client application, my React application to also work with this type information. So if I come down into my TypeScript application, I have say a page where we're going to create a new game. So this is gonna need to perform a mutation against our GraphQL server. So it's going to need to create a new game. So what I've done is I've created a reusable little bit of GraphQL query that's reusable called create game. That is just the mutation, create game, and this is what it's gonna do and it's gonna return me the game ID. I don't need all the other fields, I don't need the questions at this point in time because I only need the ID to go through to the next screen. Inside of my conflict YAML file, so my code gen YAML file for GraphQL code generator, I've also said that I've got to generate a TSX file, which is going to contain some React hooks. And it's gonna use the TypeScript plugin as a TypeScript operations plugin. So it's gonna load these files, these operation files here. So these are those pre-written queries and mutations. And then it's gonna generate me a typed document node. So this could generate me specific React hooks if I wanted them as React hooks or pyrolycompones and things like that. But I've told it that I'm not gonna use those plugins at this point in time. Instead, I'm just gonna return typed document nodes. So these document nodes can be loaded by anything that's gonna be consuming GraphQL, whether it's React or Angular or Svelte, et cetera. I'm just, they said so having to be doing this with React. So this create.game document is the thing that's been code generated for me. I can then pass this to a used mutation which is from the Apollo client for GraphQL. So this is a custom React hook that Apollo has given us. I also have just some state, some normal like React state that's available to me as well that I'm putting some type information that I'm putting some objects in. And these are combining together to create a strongly typed version of a query against that GraphQL server. So I have my create.game operation that's here that when executed, so depending on when an effect is streamed. So someone has clicked the button to actually create the game. It's going to call that hook which is gonna perform some operations. Eventually we'll get back some data. And that data is going to have, if we have a look at that getting unpacked, here we go. Down here on line 21, that gets unpacked. We're not loading and we've called and there wasn't an error and we received the create.game response. Then I know that I have an ID field here which is gonna be a stream which is the value that I get back from performing that mutation against my GraphQL server. So this might seem kind of a lot of parts that are all connected together but it all comes down to relying on the code that I'm generating out of here looking at a schema that I've got here combining that with some pre-written commands and operations that I've got with inside of my GraphQL application. So the client has some pre-written operations working with a schema that I've got elsewhere and we're using the types end to end across those so that I can generate out. Type safe resolvers. So these are the resolvers that are gonna be returning custom model mappers so that I can map the underlying data structures of my database through to the types that my schema says are available to the clients and eventually have a client that is capable of using these operations generating some typed information of them and then when say a mutation is called I get the data back and I get that data back in a typed way that I can have that field available to me and I know what it is and I can perform the correct step through in my application to get to the next point. So I know that I was a real lot of information that we covered in a very short period of time but I hope it gives you an idea of how you can do type safe end to end GraphQL applications. We looked at a subset of a workshop that I've got. There's about eight different modules in that workshop that cover off how to build a GraphQL application with TypeScript all the way from the first setting up of the server through to how we do that end to end type safety with a React application talking through to the GraphQL server backend. So the link's there on the slide if you wanna go check that out and have a bit more of a poke around in the code that was used inside of this demo. The way that I approached this was using GraphQL code generator. I like to go schema first in the way that I build out my TypeScript, sorry, my GraphQL servers. So I tend to generate a GraphQL schema file and then wanna export that as type information but there are other ways if you wanted to do more of, use the TypeScript objects that you're creating. So like classes and things like that and then use that to generate a GraphQL schema from it to kind of go the other way. And so there are projects out there that will do that that I haven't played around with much but yeah, if you prefer to do more of a code first approach rather than a schema first approach you can definitely tackle it in that regards. If you're wanting to learn a bit more about how you can run GraphQL on Azure there's a link to a blog series that I've been writing that looks at all the different aspects of how we can do things like well actually taking this workshop that I've got here how you can run that kind of a code base inside of Azure as well as how you can run things on a server infrastructure rather than a server's approach. But that's all that I had time for today and all I wanted to cover off. Thank you for sticking around for the session. Thank you for letting me present some of my learnings that I've had when building strongly typed GraphQL applications. I hope this has been interesting. I hope you've learned something and I hope that it shows you that it is definitely possible to use the type of information we've got in GraphQL with a server and a client all together so we can do end-to-end type safety. Bye for now.