 Yeah, thanks everyone for coming as Josh said we're going to be talking about building an API with GraphQL for So who am I? Probably everyone most of you know I'm Adam one of the development leads at previous next You see me on slack and On dripple.org as AC Bramley Today we're going to cover how to kickstart your GraphQL API How do you write write your own data producers? How to write secure mutations to update create and delete content on your site and as we go through this stuff There's going to be live demos of how we integrated all this stuff into our react apps And just a little side note. I was born and raised in Wellington So it's pretty amazing to be speaking here and be back and and seeing all your lovely people. So First up what actually is graph QL? This quote is from graphql.org graph QL is a query language for APIs and A runtime for fulfilling those queries with your existing data so QL is query language and Graph is actually just referring to the way we're modeling our data So it easily maps to real-world data and mental models about how our business domain is defined So we define it as schema which similar to like object-oriented programming can kind of just be thought of as a Collection of types or classes and those reference other types and then those reference other types and so on and so forth So our schema sort of defines the interface and then we have a freed the freedom to implement the back end however we like So an example of a schema for something that we're actually going to talk about later We've got a resource. It's got a bunch of fields. Some of those fields are referencing other types which have their own fields so on and so forth and What's really nice about graph QL is you can get all of this information in a single query really nicely structured So here's an example of Give me a resource with this UUID Give me all this information and then you get this really nice Jason Response back and you can really easily traverse everything And we'll go through more of this stuff in a little bit But oh so yep, so for this presentation I'm going to talk about what our main use case for graph QR was I say main in princess because we this is the first thing we use it for We ended up using it for basically everything else on the website because we loved it so much If anyone saw my talk last year at Drupal South Brisbane On open search this stuff is based on the same project as that was so you might see some familiar design elements and so forth So what was the actual feature? Well the client wanted us to add an a save Option to content on the website So being able to save resources to what we call a collection in this case resources Were just different content types one is literally called resource, but there was a bunch of other ones So we needed to be able to save a resource With that with this heart icon on a on a page The icon needs to show the status whether the page is saved or not And then when you save or unsave something it needs to update on the fly You needed to be able to save a resource to one or more collections through a modal UI like this You need to be able to create new collections as you're saving content content to collections And of course you need to be able to see your save resources and manage those resources and collections as well In this scenario collections are specific to a user So the user owns the collection. They're the only one that can view it and manage it and so forth Except there was also a feature that needed to be able to share a collection So a user should be able to toggle on link sharing and then they can share that collection with the public In Drupal this was just pretty simple access control stuff, but We'll talk about how we actually built all of that to begin with You might be wondering why don't we just use JSON API and we actually did The first sort of proof of concept for this feature was using JSON API It just didn't turn out that great and we had to do a lot of manual stuff to get it working We thought screw it why not try graphql It was a new sort of bleeding edge project anyway. Let's give graphql ago So as an example of one reason we didn't really like JSON API was if you've worked with JSON API You'll be well and familiar with something the payloads that it sends back Can be pretty hard to manage in your front-end There's a lot of data normalization that you've usually got to do Especially when it comes to things like query caching making react react your act react apps nice and snappy Jason API was just very difficult to work with whereas graphql exactly the same data Very much very easy to read and understand what's going on for both your developers and your software We also found that creating and updating operations were quite a lot more cumbersome with JSON API We needed to write a lot of access controls around You know making sure people can't create collections for other users So on and so forth again lots of manual data manipulation in the front-end to make things more manageable And it became really hard to keep track and keep data in sync with case like query cases and Things like that with the responses coming back from Jason API and I'll cover that a little bit more again in a bit So to LDR Jason API is a really powerful tool out of the box But it's sort of this broad largely undefined API that you just got to figure out how to use Whereas graphql requires much more upfront development time for your especially for your back-end developers But it just pays off and dividends later down the track So into the graphql module on Drupal org. We're using the graphql 4 module as a put in my slides The difference between graphql 4 and 3 is Graphql 3 version 3 shipped with a lot of things out of the box that defined Schema and all that sort of thing for you Whereas graphql 4 is more of a really a toolkit to empower your developers to design the API really from the ground up a Lot of that automation schema stuff like that has been moved to the graphql compose module I believe I haven't used that though So check that out if you need to Cool, so let's actually get started into some graphql stuff. So You'll spend most of your time working in the back-end anyway working with your schemers and schema extensions This get book by the way I've got to mention it in the last slide is an awesome piece of documentation that covers all these concepts and a lot more and Yeah, highly recommend you'd use that for for further information as I said though We're gonna start with schema and schema extensions So your schema is actually just a PHP plug-in in your code Literally just looks like that. You don't actually have to implement anything This is just what you're tying all of your schema extensions to and what you're configuring In your graphql server. What it's what's called in the Drupal UI So when you're creating a graphql server You define what schema it's tied to and then you enable all of these schema extensions And we're going to talk very very in-depth about these schema extensions because they're probably the hardest thing to Wrap your head around When you're building one of these API's with this module So first up we need some graphql schema. So We this is for again saving those resources to a collection So this is our example of our collection schema So if we look at the collection type got a bunch of pretty regular fields Then we've got things like the owner which is referencing another type, which is an author That's another graphql type that has his own attributes so on and so forth The the most interesting one is this items field Which is another type that we've defined at the top, which is actually what graphql considers a union type And that's just a union of a bunch of other types. We've already defined in our code base as well Then we've got this collection list, which is going to be the type of data we get back from when we're querying for a list of collections and Yeah, we're going to demo that in a sec So it's sort of connecting the dots a little bit You've got this collection extension dot base dot graphql file that goes in your graphql folder in your custom module Then you've got all your schema extension plugins underneath a plugin directory Inside those schema extensions is where all your code lives that is tying your graphql schema to how that field is then resolved in Drupal and Hope you're ready because We're really going to get into it now So a schema extension is really as I said how you're defining How graphql is resolving the fields on each of your types? We opted to go with a extension per type Although there are like common Some common types like the author or the term where you've just got you know a simple name and ID And we stick that in sort of a common extension Plugin All of our extensions extend a schema base Class which I'll go over again in a minute, but basically when you implement one of these schema extensions your You're implementing this function called register resolvers, and that's where all your logic lives about how Drupal needs to resolve each of your fields for your type for your given type So let's have a look at what one of those might look like So this is probably One of the easier Well, it is probably the easiest Example that I could find in our code base and even this is pretty hard to wrap your head around when you see it for the first time So I'm going to try my best to explain all this to you But obviously if you have any more questions after this feel free to come up to me and pick my brain But basically for every field on every single one of your schema types You need to call this registry add field resolvers the registry object is passed into your schema extension and This ad field resolver Function is basically saying hey GraphQL. This is how you for the collection type For the field for the ID field. This is how you resolve the value of that field So this ad field resolver function takes three arguments the type The the field and then this resolver which is really the meat and potatoes and how the field is actually mapped So let's dive into that particular thing even further So this is where the naming for things gets pretty confusing the builder object is just something you construct in Your register resolvers function. It's just comes from the GraphQL module But what we're saying here is We're saying for the builder produce me a Entity UID data producer proxy. So the produce function is a factory method for returning a data producer Data producers are explained in much more detail in the handbook But other producers are basically input output functions that say you've got this Entity in this case Give me the result So produce is giving us a factory of a Sorry, it's a factory function for a data producer proxy We're asking for the entity UID data producer and then we need to tell GraphQL how to or the GraphQL module how to map each parameter on that data producer So in this case the data producer has one Parameter for its result function, which is entity and we're saying map that to this builder from parent Which is basically a magic method of saying give me the parent entity or when we're in the context of Composition it gives you the result from the previous data producer, which will also cover soon So breaking that down even further to really try and understand this because this is honestly Probably one of the hardest things to wrap your head around when you're building an API with GraphQL We're getting the parent value We're getting this data producer proxy object and we're saying hey this is how you map the entity parameter in that data producer and Then we're adding that For our collection type for the ID field So this is what the entity UID data producer looks like this comes with the GraphQL module and with a whole lot more As you see we've got an entity parameter. That's what we're mapping all it's doing is outputting a UID So that's one example of how to map a field Now the example for the name very similar We've got an entity label data producer and we're mapping that to the name field for the collection type Hopefully I explained that in enough detail that it's starting to make sense And this is what you can kind of do with that data right you can get the name and the ID for a given collection Quickly covering the schema extension basis So we basically implemented like a base class which all of our schemas extend from I highly recommend doing this for really simple fields like ID title author created updated date It's just really going to reduce the duplication through all of your schema extensions And make things a little bit more manageable Okay, let's get a little bit more complex so There's also an idea of data producer composition so you can compose multiple Data producers together to refine your data even further So this means you can write really generic data producers that do really specific things like their entity UID and then you chain them together for Really powerful field manipulation So we take a look at an example here We want in this case. We're mapping a field that we want the absolute your relevant entity for So we're saying we're using the builder compose function up the top And that just takes a arbitrary list of arguments each one of those arguments is of course a data producer So we're using the the builder produce function again in this case We're using the entity URL data producer which again is shipped with GraphQL and we're saying for the entity URL Data producer map the entity property for the builder from parent again Which is going to give us our collection entity and then map the options parameter to a hard-coded value Which is just setting the URL to absolute You need to use this builder from value function here to actually pass a hard-coded value. You can't just pass the array That's just telling GraphQL how to resolve that that field again that parameter and then we're composing that with the URL path Data producer because we obviously can't return a URL object in our GraphQL response. We want a string So we're composing that with URL path and saying map the URL property for URL path to the parent Which is going to be the result of the previous data producer You can kind of think that of this is like a migrate process plugin pipeline We've got a bunch of plugins that do very specific things and you chain them together to get what you need The URL path one does other things like cacheful metadata bubbling and all that nice stuff for us So, yeah, super super handy this this idea of composition as well Cool, so we've discussed your schema how to resolve your fields now We're going to get into actually how to create read update and delete your data Going back to that that first very first example we talked about with saving resources to collections So let's start with read always the easiest one, right? So first we have to define our schema again in this case. We're defining two different query types We're saying for a single collection. We can query that by an ID and For mold getting multiple collections. We need to In this case get a list of collections for a given user Which is the owner parameter and then we've got some pagination options there as well So then again, we've got a wire that up with a field resolver In this case the type is query and the I the field that we're we're resolving is in this case the collection Query which is what we define in our schema before and Then we're using these awesome magic data producers to say in this case We're using entity load by UUID, which is again shipped with the GraphQL module And we're saying map the type parameter, which is the entity type ID to this hard-coded value And then we're using we're mapping the UUID parameter to this builder from argument So that's going to take the ID argument from our query pass it into the entity loaded by UUID data producer and return our collection objects in our case That's going to then resolve to the collection type and then as we I'm going to see in a moment. We can then query all the fields on that Just by all that schema. We've defined before a little bit more complex with the listing collections by owner So this is a custom data producer that we had to write because obviously GraphQL I doesn't know we need to be able to load collections by a user to write this ourselves But this is where you can get and start to see the power of all these data producers and and how everything's resolved together We could just say map the owner argument to the owner parameter in our data producer and then load our user object in our custom data producer But we're actually going to take it a step further and say why don't you just load that object for me and give me the loaded User object which is what this kind of middle chunk here is doing it saying again use the entity load by UUID for Data producer take the owner argument from the query And I'm going to get a actual loaded user object in my data producer and then just map the other two They don't really matter that much So this is our actual data producer This is so when you use it when you're writing a data producer You know you've got the class and the plug-in definition, but you only actually have to implement one function Which is resolve and as you can see right at the top We've got the owner as an object rather than a UUID, so we don't have to load it manually And then we do all of our querying we add our cache data metadata and then we return this query connection object Which is again provided by the GraphQL module and all that does is does some fancy Deferred loading and things that I'm not going to pretend to understand But it works very well All right, so we got all that out of the way, and we're going to do a little bit of a live demo So we're going to pray to the demo go on Do this? This is going to be very tricky. I didn't think about this, but Okay, so this is an example when you install the GraphQL module and you set up your server You get this Explorer tab and that gives you a GraphQL a graph IQL interface where you can you know debug Your queries your mutations and everything like that. So this is running that collection query I'm saying give me the ID and the name for this collect for this UUID If I go over here, I can see I've got the owner and I can ask for the name of the owner as well And that's going to give me back the loaded user object Okay, I'm going to have to really really speed up because this is going to and Okay, we'll just we'll just leave it. Oh god. This is a terrible idea Shit, I'm just going to do this and then we're going to wing it. All right. So creating This is using something called mutations in GraphQL It's really just a fancy term It's nothing too special. It's all set up basically the same So we start with our schema. We've got input and output now and then we define this mutation type We're calling it create collection in this case and then mapping the the input and output Then again, we need a data producer. So in this case, we're going to get a name and a description we're going to do some validation and we're going to save the entity and Then return that saved entity then we need to wire it all up So again, we're using this field resolver the type in this case now is mutation The ID is what we define in our schema and then we're going to use our custom data producer and map The values using this kind of fancy little anonymous function All that does is takes an array of data and returns the value for a given key So what about that validation that I kind of skipped over? Well, again, there's a page on that in the get book However, we do it a little bit differently So the first Yeah, there's a few ways to do it. So the first way is sort of following the The conventions in the get book where you sort of set up this Collection response object and then you can do, you know access control Stuff like that and add this violation using this ad violation method So our collection response is just a class that extends a response class in the GraphQL module Next you could just literally return an array of errors that kind of works as well What we actually like to do though is throw exception So the user error exception is a game ship with the GraphQL module and it's considered client safe So the error messages in these exceptions are going to make their way to the front end And then you can actually expose those messages to your user. So you've got nice meaningful errors when things When your validation fails The main reason why I really like this approach is because your front-end components actually know there's been a problem in your mutation So we use the tan stack query library Which is yeah unreal. It's amazing. We use that ships with this use mutation hook Which is a react hook for firing off a mutation In the first two examples of validation that error Boolean that is returned from user mutation. That's going to be false if your validation fails, whatever reason Don't ask me why also on error is not going to be called It's going to basically go go into your on success callback every time Even if you've thrown errors or returned errors in your GraphQL code if you're using throwing exceptions your Your request is still going to return a 200 but you're going to actually get an error Boolean back You're going to it's going to call that on error callback. So you've got a lot more a lot easier separation of concerns And you know you can do nicer things Like this. I'm so glad I recorded this one Because we're not doing any more live demos because that was a train wreck So we've got validation to say hey, you're only allowed to create a hundred collections If someone goes to try and create another one. No, sorry. You can't do that We can display a nice error message. We can get rid of the modal That sort of thing. That's just one thing you can do with validation So update I'm going to smash through this again We've got this is for toggling that shareability of a of a collection again. You define your schema You write your data producer in this case. We're doing validation again We're just setting a property on the entity and we're saving it and returning it again And then you add your field resolver, which is very similar to before and Finally delete Yeah, basically the same thing right you get your schema. Yeah, yeah field resolvers and Yeah, you're stuffing. Yes your data producer So I trust you've got it by now All right, and then we could probably do a super quick little demo Of the whole thing so we we didn't actually get to showing like okay saving a resource to a collection so And in this tab, I'm just logged in as a normal user. So this is the mentally healthy workplaces website This is my local environment obviously as a member of the public you can go and sign up to and Create an account on this website the URL is beta dot mentally healthy workplaces dot gov.ru Or just search mentally healthy workplaces and it will come up So you can create an account and then you can start learning so this whole websites around You know giving resources about mentally healthy workplaces We're not going to cover that right now Don't have enough time So in this case, I've got logged in as a user. I've got one collection. Everyone also gets a default save resources collection So I'm going to go over to this collection. I've got two resources here if I go to this resource Do to do of course I get a giant error and why wouldn't I We've got this nice save button here So when we click that we get a modal and this is these are all react apps by the way If I quickly look over here, this is one of the cooler things that I wanted to demo Please God work So this motor itself is a react app and we want to say we want to save this piece of content To a new collection. So we're going to create a new collection. We're going to give it a name nice and meaningful and Then I've got the network tab open because I wanted to demo something which is Why I think GraphQL is so extremely powerful and yes, you could do this with other things but GraphQL makes it very easy So I'm going to create that collection and You notice. Hey, I've got the collection already in that modal, but I've only actually done one request and The reason we can do that is because GraphQL allows us to return Really nicely structured data. So we've got all the data we need for this collection Directly in the response from our mutation. We can just take that let it that object and put it into our query case We don't need a refetch that uses collections because we know this new collection is just going to be in the user's Users collections Therefore we can just add it to our query case. That's going to cause react to re-render and then it's just like we created this new collection Sorry that we It's just like we fetched that, you know new collection from the list. So finally of final bit of the demo I'm going to say I don't want that in that collection anymore. I wanted a my new one Save that that's going to do fire off multiple mutations which can be executed in series So you can do more than one mutation at a time You might have seen those little alerts pop up that say we've added or removed things from our collection Again displaying the correct status And Then if I go back to my my hub page, it's got that new collection here and Then we've got the save resource And This is the toggle share functionality as well Let's toggle it on copy the link go into a new That was the wrong new tab a new container That one no, I'm not logged in anymore. You can just trust me that works. I guess For some reason it's not playing pressing into cool. I think we've run out of time anyway, so that was about it Cool any questions a graph you are Yeah, I think we're like I guess a lot of the automation stuff like building all that schema is It is very hard to wrap your head around. That's probably the hardest thing, you know all the data producers and that's what I really wanted to hopefully ease anyone into if they're if they've kind of looked at that stuff and thought crap that looks confusing as hell and yeah, just Show you how powerful that stuff can be like it's very very worthwhile Yeah, I think what some of that kind of stuff is in the GraphQL compose module They've got like automated schema generation and all that kind of stuff You're obviously not gonna have as fine grain control over any of it, but yeah, definitely have a look to brahm Yeah, I've just I've got yeah all of my stuff is fully tested So we just use functional tests and then just part like the great thing about that GraphQL I sort of query interface is you can write all that GraphQL Query and then you can just inspect the network tab right to see exactly what is actually being sent and then I use that to put Into my functional tests and then I just Do those same requests just make sure you know data is coming back or errors are coming back and that's what that sort of thing so And then all of the front-end components are reacts and all that stuff We obviously we can use like mock service worker, which is a way to mock all of your responses and React or jest so and then we use that for the testing the actual front-end components I definitely wouldn't diss this stuff and like JTB or something like that So a 10-set query or it used to be called react query, but now it's used for it can be used for Anything I think there's like view and angular and stuff like that So that that example. Yeah, I didn't want to get into the react code because obviously there was shitload of stuff Sorry, it's a lot of stuff to get through But react query is like a way of As a library to query for stuff and it comes with a lot of like query caching Stale cache optimizations retries all that sort of thing for free and it's really easy to use So yeah, one of the best libraries out there for the front-end tooling