Your First Federated Schema with Apollo Server

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
well hello everyone first off I'd like to start by saying thank you so much to that pollo team for inviting me to give this talk today so a bit of my background before we jump in again my name is Mandy and I've been using graph QL since early 2017 and I started teaching other people how to use graph QL shortly after that at a bootcamp I used to work at fast forward to late spring 2019 I decided I wanted to consolidate everything I had learned about building complex craft QL api's over the years into a book and I ended up getting about a week or two of work done on the book before I woke up one morning check Twitter and saw that Apollo Federation had been launched after I took a look at its documentation I promptly decided to throw it all the content I had just developed on schema stitching and ended up basing the book on Apollo Federation instead so today I'm going to show you just how easy it is to get up and running with Apollo Federation so on that note I'm just gonna flip over to keynote now okay well let's dive in so the roadmap for my talk today is that I'm gonna give you a quick rundown on what Apollo Federation is and why we would want to use it we're gonna do a little bit of a demo refactoring of vanilla Apollo server into one using Apollo Federation and then we'll finish off with a little recap so let's start off with answering one of the big questions what is Apollo Federation so Apollo Federation is a set of tools that allow us to compose multiple graphical schemas declaratively into a single data graph now that's a bit of a mouthful so let's explore what that means a bit more visually so when using Apollo Federation we're gonna have multiple implementing services and each of those services are going to have their own graph QL schema in front of those services will add a gateway that composes those schemas into a single federated data graph so that clients perhaps a web client and a mobile app client can then query a single graph QL API without needing to worry about the implementation details of the underlying services so what problems does this solve well one of the most compelling reasons to create a graphical API in the first place is that it allows us to expose one data graph potentially with multiple data sources behind it like a REST API and a sequel database and then we can query the data in a way that reflects the relationships between the notes in the graph however as we expose more and more objects and their relationships through various types and fields it doesn't take long for even a moderately complex app to require a large number of type definitions in turn it becomes difficult for multiple people or teams to collaborate on building the API and what's more the graphic UL API then becomes a single vulnerable point of failure in the system the solution would then be to create a distributed graphical architecture and break up the schema into multiple smaller services for development purposes but then recombine each separately managed portion of the schema into a gateway API so that client applications can remain agnostic to these divisions when querying data before Apollo Federation there was an existing solution to do this called schema stitching however with schema stitching there tends to be a necessity to split a schema sometimes awkwardly based on types and it also requires a lot of boilerplate code to effectively consolidate the data graph and that again introduces a single point of possible failure into our application architecture Apollo Federation addresses these issues by allowing us to split a schema based on separation of concerns rather than by type and it also takes care of the heavy lifting when it comes to combining federated schemas into a single data graph for clients to query so it really is like having your distributed graph QL architecture cake and eating it too now Before we jump into the demo we're going to need to wrap our heads around a few key Federation concepts the first is the notion of an entity an entity is a type that you define canonically in one implementing service and can then reference and extend in other implementing services entities are the core building block of a federated graph so the things that allow us to split schemas based on separation of concern rather than by types alone and we're gonna create entities using an add key directive in our schema once we define an entity we can then reference it from other services as needed other services that reference the entity will only be able to return a representation of that entity from any corresponding resolver so the Gateway is going to have to hand this representation off to the entities originating service which means we're gonna need to provide what's known as a reference resolver in that originating service to fetch the data for that specific entity and last but not least we can even add additional fields to a type originally defined in another service by extending that entity in a referencing service okay so to create a federated data graph we're gonna need to make use of some additional Apollo libraries so first of all we're gonna need the Apollo Federation library which allows us to make our services schemas Federation ready we're also gonna need the Apollo Gateway library which is going to help distribute incoming graphical API requests to the underlying services and lastly we're still gonna need Apollo server because we're going to need an instance of Apollo server for the Gateway API and each of the implementing services we create okay now that we have some awareness of what a Paulo Federation is and why would want to use it let's walk through a quick demo of refactoring a regular Apollo server into a federated data graph with to implementing services so over in a BS code I've already set up a basic graph QL API and this graph QL API wraps a REST API that provides us a little bit of data about Apollo astronauts and their various missions so I hopped over to Chrome we can see that we have an astronaut's endpoint here that will give us an array of astronaut objects containing their names and we also have a missions endpoint which gives us some data about the missions including their designation a start date and end date as well as an array of crew member IDs which are references to the astronauts IDs over here now in graphical playground I already have this graph key level server running so if I query the astronauts by name I can in fact see that list of astronauts listed here okay so back over in PS code we're gonna start working through the steps of converting this into a graphical API that uses Apollo Federation so the first thing I'm gonna do is stop the server for a moment and I'm going to create a new file to contain all the code for our astronaut service and I'm gonna start by doing a little coffee pasta here I'm gonna grab all of the code from our index.js file and I'm going to paste it over into astronauts jeaious now we're going to need to make a few changes to this file to turn it into an implementing service so the first thing we're going to need to do is import build federated schema from that apollo federation library the next thing we'll do is we'll set a unique port number for the service because it's gonna need to run on a different port from the gateway api then we're going to make a couple changes to our type definitions so the first is that we want to turn this astronaut into an entity so to do that we'll add the key directive and set its field to ID which means that whenever another service reference is an astronaut as long as that service knows the ID of the astronaut the gateway is going to be able to help us resolve that field next step we're going to put the extend keyword in front of type query because the query and mutation types originate at the gateway level of our API so the Apollo documentation says that all implementing services should extend these types with any additional operations next instead of passing the type deaths and the resolvers directly into Apollo server what we're gonna do is set a schema property with a value build federated schema and in to build federated schema we'll pass an array containing an object which has our type deaths and our result is in it and then last but not least we're going to update this console log message just so that it's obvious that our restaurant service is ready now we're going to need to make some changes to our original index.js file to turn that Apollo server into a gateway so the first thing we're gonna do is we can get rid of this fetch import because we won't be making any direct request to our REST API from this file anymore we can get rid of the gql tag import we can get rid of this constant here we can also remove the astronaut type definitions and resolvers but what we're going to need to add is an Apollo gateway so to do that we're going to import Paulo gateway from the Apollo gateway package then we're going to create a new gateway by instantiating a new Apollo gateway and into that Apollo gateway we will pass a service list which will be an array and inside that array will be objects representing each of the implementing services that we want to add into our single federated data graph so our first service will have a name of astronauts and a URL of HTTP localhost 4,000 line now in our Paulo server we no longer have type definitions or resolvers to pass in but we do have a gateway and we're going to set subscriptions to false because at this time Apollo Gateway does not support subscription operations give that file a save and we have one last thing to do before we can start up our graph QL API again which is to go into package JSON and we're gonna add a script to start that new astronaut service so we'll use node Mon to start up at astronauts service ok with this code in place we're ready to start up our graph QL server again so we can npm run server and we can see that our astronaut service has started up now and if we hop back over to graphic you'll play ground and run our query again we'll see that everything works exactly as it did before okay now that we have our astronaut service up and running let's go ahead and add a mission service so back over NPS code I'm going to stop our graphical API momentarily and I'm going to create a file to contain the code for our new mission service and inside of missions j/s I'm gonna add some boilerplate for creating our new service so what we see here will look pretty similar to what we just saw in the astronaut service except of course we're going to need to run this service on its own unique port and our mission service is going to be responsible for managing a mission type so we're gonna go ahead and add that type definition as well as queries for getting an individual mission or a list of missions and we're also going to need some resolvers for fetching that data from our REST API and they're gonna look pretty similar to what we saw in the astronaut service so I'm just gonna copy and paste those and again to save a bit of time so with our basic mission service setup we can go ahead and make a connection between our astronaut type and our mission type and we want to do that so we can add that crew field in to our mission type and from that crew field return a list of astronaut objects so we'll add a crew field which will be a list of astronauts now to use the astronaut type in the service we're going to need to define it in the service as well so we will use the extend keyword to define our astronaut type here and once again just like we did in the originating service we're going to need to add a key directive and set fields to the ID of the astronaut and because we're using the ID as our key we're gonna need to redefine that ID field here but this time we'll put at external directive on to it so that Apollo knows that this field was defined over in the originating service now our code won't quite work as it is right now because we need to resolve this crew field inside of the mission service so to resolve this field with Apollo Federation we only need to return an object or in our case a list of objects representing all the crew members that contain a type name property and an ID property to identify that object when the request is forwarded onto the astronaut service so to do that I'm gonna scroll down to my resolvers here and add a resolver for the missions crew field now in this resolver we map over that crew array and return a new array of objects which contain a type name property and an ID property the next piece of the puzzle is to add what's known as a reference resolver to the astronaut service so if I jump back over to my astronauts JS file I can go ahead and add that reference resolver for the astronaut type which is gonna be a method called resolved reference which has the reference object as a parameter and this resolver is going to actually need to do the work of fetching that data from the astronauts endpoint just like we do here below for a single astronaut query but this time we're gonna pull the ID off of that reference object so before we can start up our growth again we're gonna need to add our new mission service to the gateway to do that we'll add the mission service to the service list giving it a name of missions and it will be running on port 4002 and over in package.json we're gonna need to start script for that as well so we can call that missions and we'll be using the missions JS file here ok so if you run npm run server now we'll be able to start up our gateway api as well as our two services jumping back over to chrome if we go on graph QL playground right now and run a missions query we can see that we get a list of missions with their designation names and we can even add a crew field now and get that list of the crew members with their names too so this is looking pretty good but it would be great if we could traverse the graph in the other direction too from astronauts to their list of missions to add a missions feel to the astronaut type we actually don't need to touch any of our existing code in our astronaut service we can actually extend the astronaut type directly from within the mission service with an additional field containing that list of missions so if we head back over to the S code now in our missions J's file we can scroll back up to our type definitions here and add a missions field which will be a list of mission objects and then once again we're going to need to resolve this field from within the mission service too so we'll go ahead and add a resolver for that new missions field on the astronaut type and we'll have to keep in mind that again we only have access to the data about astronauts that exist within the context of the service in other words we only have their unique IDs so due to some funkiness with the Mach dressed API when it comes to selectively querying resources based on a value inside of an array field we're gonna have to sell for fetching all the mission data and then filtering out the mission objects that don't contain the astronauts ID in the crew field but we're dealing with a small amount of data here so this is okay for demonstration purposes I'm also going to make this resolver async so we can use a weight inside this resolver so it's a little clear what steps were going through okay so our first step will be to fetch that missions data and we're gonna go ahead and get that missions data from our missions endpoint like we have before and then we will parse that response so what we're gonna do is return a reference for solver by mapping over the missions array and returning only the objects that contain the astronauts ID in the crew field so to do that we will return missions filtered wildy structure the crew field out here and we're only going to keep the mission objects where the crew field includes the astronauts ID and we're going to need to parse that ID as an integer okay so if we hop back over to graph QL playground now if we go to the astronauts query we can now get a missions field with the astronauts query and let's get the missions designation the name okay so now we have the astronauts and we can also see what missions they participated in and that's it we now have our federated data graph in our whay api up and running so just a recap what we walked through today we created a federated data graph to implementing services and an astronaut entity we referenced the astronaut entity in the mission service where we used it for the crew field on the mission type and we even extended the astronauts entity in the mission service by adding a missions field to it so we can traverse the relationships in this graph in both directions so if you'd like to see the complete code for this demo I've posted it over on my github here and that's all I have for you today thanks so much I'd be happy to take some questions now okay so I've been keeping track of some of the questions that have been coming through zoom so the first question that I have here is from John and the question is can Gateway work with graph QL servers that do not use Apollo and might not even be written in JavaScript so the Apollo Federation documentation actually has a page on this called third-party support for Apollo Federation and in the documentation they provide a list of all their known open-source graphical server libraries that have built-in support for Apollo Federation so I would encourage you to take a look at that the next question we have here is it mandatory to redefine the ID field locally at the service level and even the resolver to pull it from the Gateway can it be inferred from the at key directive so it's another great question that's asked by Andrea so I yes you have to redefine the ID field locally the field that you define with the key directive is actually pretty interesting so in the example that I gave in my demo we just used the ID field as the key but you can actually define multiple fields as as a key I'm just by separating with them by space within that field value so yeah definitely take a look at the Apollo documentation if you're interested in doing something like that so the next question I have here is if the referenced objects are a list is it possible to retrieve them from a list endpoint rather than getting each of them separately and this one was asked by Tom so thanks for your question Tom so I'm not sure if the this I saw this question come through while I was still giving the demo so I'm not sure if the remainder the demo helped answer this question for you but the key with the referenced objects is that you need to make sure that from the resolver you're returning an object with that contains that tightening field as well as the ID so if it's a list obviously they're going to need to be in a list format or if it's a single object you would just return a single object from the resolver and you can see an example of that again in the Apollo documentation so hopefully that helps answer your question again how you end up ultimately resolving those referenced objects in the originating service is going to be up to you okay let's see if we've got any more questions coming in here if I'm using a data loader are the astronaut missions request batch so this is another great question so this there's some different dimensions to this so under the hood the apollo gateway is doing some work to batch request that it's sending from one implementing service to to another implementing service however once that data gets to the implementing service the same rules still apply with respect to how you're going to need to batch operations to reduce the number of requests you're making to your database or whatever the case might be so yeah there's some auto magic batching of requests happening under the hood between services but once that request gets to your service its 100% on you to figure out how you're gonna deal with that using data loaders let's see I'll see we have four questions here if multiple services have the same exact operation or field name how would you specify which one to use when composing your query so I think I might answer this question in a more general sense because you might be wondering about also redefining types across multiple services so when Apollo Federation first launched did some initial experimentation and found that you it was a bit tricky to try to have types objects interfaces enums whatever the case might be defined in one service and then have the same type used in another service initially it would throw errors but there's been a ton of work done on these libraries over at the last year or so and there's now support for reusing different types across services which is amazing um so you can reuse things like objects and interfaces and scalars and unions across services there's just different rules for how exactly you have to go and redefine those types across the services so again I would just encourage you to take a look at the Apollo Federation documentation for those specific roles okay so I'm gonna take one more question take the question from John here is it possible to run your mission service on its own in its own graph killed playground so yes if you went to localhost 4000 one or four thousand two to look at the astronauts service or the mission service directly you could actually see it there in graphical playground okay so that's all I'm gonna take four questions right now cuz we're on 11 a.m. here right now so I am going to hang around in the slack Federation channel for a little bit to answer some additional questions if you have any you can just throw them in there and now I'm gonna hand things back over to our MCS even Alex
Info
Channel: Apollo GraphQL
Views: 31,339
Rating: 4.8624339 out of 5
Keywords: apollo graphql, graphql, apollo graphql server, federation, graphs, schemas
Id: v_1bn2sHdk4
Channel Id: undefined
Length: 26min 22sec (1582 seconds)
Published: Mon May 04 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.