Building A Distributed Graph With Apollo Federation - Gerwin Brunner
- David Krehling We're back everyone.
Did you guys like your little break?
All right, so now we have two speakers coming onstage, Gerwin Brunner and David Krehling
on building Apollo graphs?
Got it.
OK, cool.
Let's give them a warm welcome [applause]
>> Gerwin: Hello.
David: Can you hear us?
Great.
Gerwin: Yeah, I know what they meant before.
You're in the spotlight literally.
Is there anybody there?
David: All right, perfect.
So yeah.
So, yeah, thanks for coming, I guess.
Today we're going to talk about building a distributed graph with Apollo Federation and
maybe before we dive in, some questions for you just so we get to know you a little bit
better.
So actually, start with simple one.
How many of you actually know GraphQL?
OK, raise your hand.
OK, so I can't see anything.
OK, that's kind of good!
How many of you already use GraphQL?
Yay, also, quite good, I guess.
And how many of you have heard of the Apollo Federation.
OK, that's more than I expected.
But that's good, because that is why we're here.
Maybe just to start with us.
My name is David Krehling.
This guy next to me is Gerwin Brunner, we're both from Vienna Austria, and we work there
in one of the biggest banks in Austria and central Eastern Europe region and we're building
a state of the art web application replacing an old legacy one and we tried to incorporate
GraphQL like from day one into our design so we got quite a few learnings and basically
this talk incorporates that a little.
So maybe let's start simple.
What's actually GraphQL?
OK, so -- oh, I can see it here so.
So I guess most of you know REST, and I guess most of you know like the issues you get with
REST, especially when you get like -- when you have like more complex or bigger applications
or systems.
You have several issues, for example, if you need data from different kinds of services,
you always have to call them separately, you have to get all the data from them.
And basically this leads to a slow network environment and you basically get data you
don't want or you don't need.
GraphQL is trying to solve that by putting a data graph in between which is described
in this so-called schema and that basically allows you to query several services at once
and to query exactly for the data you really need.
What's also cool, just like a side fact, you can also put GraphQL as a layer on top of
your REST services so if you can't switch like this, you can really build the REST service
into your GraphQL environment and then replace it piece by piece.
So like I said before, like one of the core building blocks of GraphQL is the schema.
Which is shared between the client and server, and basically it's there to validate all the
inputs or the payloads you define in the schema.
So every time you make a request and GraphQL terminal, it would be a query or a mutation,
the schema checks if one the types are proper and if they are input or the payload is complete,
and if there's -- if the data is in there or the data that you want.
Another thing is that it's like strictly typed, so you also have type validation in there,
as well.
The second big building block is the resolver, and basically in the resolver, not all of
the metric, but basically all the metric happens because it's there to process data, and to
retrieve data and return data.
So every time he query something for example, it goes through the resolver and basically
that's the point where you connect to the database, where you connect to APIs, to other
services and so on.
So maybe like the people that already worked with GraphQL know that issue, that like when
it gets a little bit more complex and as it gets a little bit bigger, you create this
huge monolithic schema, which basically creates a mess.
So there are several solutions to that.
One would be to use schema stitching, which basically creates a mess, as well.
Because like one of the biggest down sides is if you try to separate the schema which
shouldn't be separated, you create like lots of overhead code, you create lots of effort,
lots of work, the bigger it gets, the more you separate t the harder it gets to maintain.
Even to understand and basically at some point it's impossible to test it.
So that's where Apollo came in.
The smart guys there, and they created Apollo Federation.
What they tried to do is incorporate some core principles, basically the first one it
should be a declarative, meaning no schema stitching at all, so you're freed of that,
and another thing which is pretty great is that you have a separation of concerns, meaning
you divide your schema into several stand-alone schemas, which then can be referenced to each
other.
And basically to bring all of that back together to the client, Apollo created the gateway
which takes all those several federated schemas, and makes like one huge schema out of it again,
so basically like the client, nothing changed at all, it's only on the server side.
OK.
So to build up federated schema, you have several building blocks, we go through this
list one by one.
Let's just start with entities and keys.
So basically an entity is just -- is just a GraphQL type basically the only thing that's
changed is this @key directive on the right-hand side of the customer.
Which basically creates a connection between the several schemas, so by adding this @key
field, you create this connection point and basically what it is is nothing else than
as a primary key, basically it works like an SQL.
So as soon as you created this entity, which again is basically just an @key directive,
you can reference it from different schemas or services, so maybe to go back, just imagine
this code example, for example, would be in a schema in the customer service, and the
next one here would be, for example, in the order service.
So basically everything you do there is just creating the same type again, but putting
this extent keyword in front of it, and again this directive.
The @key primary key at the end of it.
And that's basically where you'd just -- how do you say it, like, reference to the other
schema.
And basically that's almost it.
So all you have to do now is just declare what do you want?
So basically again, just like super-simple GraphQL and for that, you got the other.
The other keyword which would be the external.
-- I have to drink something, sorry.
Which would be the external keyword, and basically all the external is doing is it's telling
GraphQL, it's telling your schema in this case, the order schema, that the ID is coming
from a different schema.
So it's not like native to the schema.
This might sound kind of weird like in the first second, but if we go to the next slide,
hopefully it makes a little bit more sense, because there are also type extension, because
there's referencing between different schemas shouldn't be a one-way street.
It should basically go in the other direction, as well, because you want to divide it by
concern, and not just by type, so you also have the possibility, in this case, to extend
reference type, so basically meaning again we're in an order schema, we created the reference
to the customer type here.
And now we want to add some properties there, because just imagine in the order service,
we want to know the number of orders for one customer.
I mean there's no point to add this state to the customer service, it doesn't concern
it, so we have to add it to the order service and that's basically what we're doing here.
We just add another prop to a reference type, without this external keyword, so GraphQL
knows straight away, it's like native to this schema.
And that way you basically create the relationship between those different schemas, so actually,
it's super-simple.
So coming back to the architecture, actually, all you know is like different federated schemas,
and this Apollo gateway and basically Apollo is providing you with both of those.
Just to give you like a quick overview, how much code you would need to set it up, except
the code I showed you already, is basically just this.
So instead of inserting your server into your Apollo server, so basically that way you just
create a federated schema, which can, like work on its own, which is stand-alone, so
it's basically its own schema, and then, on the right-hand side, you see the Apollo gateway
which brings everything together, so in that Apollo gateway.
You put in a direction where to find it and that's it.
And it's basically not tough to set up and it brings you so much benefits.
And maybe to show you the transition from like a regular schema to a federated schema,
I will hand it over to Gerwin now.
Gerwin: So enough of the theory.
I like to do the insane stuff, which means, doing live coding and if the screen comes
up, we'll see how that goes.
Yeah.
OK.
So where do we start here?
OK.
Let's start with a little bit of looking into the code.
So our starting point is simple.
We have customers, we have coffee, like Espresso.
Or a cold brew, and we have orders.
Which combines those two things like customers and coffees together to basically enjoy the
beverage.
So that's the simplest schema we could come up with, so as we said, we had a customer
which has an ID and name, we have coffee which has an ID, a name, which is the type of coffee,
like Espresso, cappuccino, and we have a price, obviously it's not free and we have orders
that combine those and also give us the information when it was bought.
Pretty simple, pretty straightforward, then we have basically the resolvers and can everybody
see the stuff up there?
Yes?
No, OK, that's good, just to make sure.
So in the resolver, basically you would go to a database to a different API, to whatever,
to query the stuff, but in this case we don't -- I just basically hard coded it here.
So we have a list of customers, we have Paul, Mark and Bruce.
We have coffees like Espresso, cold brew and nitro cold brew and we have resolvers that
merge those.
In the same query, we can query all customers, all coffees, all orders.
And we have this thing here that basically makes orders resolve correctly.
So now let's quickly look at GraphQL.
GraphQL comes with a lot of implementation of the GraphQL API, so Apollo provides it
as soon as you throw it into the application, you get like a graphical interface and if
you haven't seen it yet, it's magic, magic meaning, like oh, hey, I just start typing
and it says oh, customers, and I run this query and I get a list of customers, amazing.
Then we can do the same kind of thing for coffees, and what kind of coffees do we offer?
And we get a list of coffees.
And I probably need the ID of the customer in my application to do something else, so
I can add stuff on the go, and that's actually one of the biggest magics -- biggest magic
that GraphQL does, it gives you the opportunity to, as a client to just select what you need.
Good.
So and finally, let's do something.
And let's take a look at the orders.
So we can do the same here, like when was it bought?
And we can also dig deep into the customer.
Who was it?
And we have all the same properties that the customer actually provides as an object.
So we can select the coffee -- the customer, and to do the same exact same thing with the
coffee and -- oops.
Well, the exact same thing with coffee, l so display it and so we have it here.
So that's really, really really cool, compared to a REST API.
Who wants to do the same thing up here with REST API?
I encourage you to show me in that in 4 and a half minutes.
Anyway.
So enough about how cool that is.
So let's just do the crazy part.
The crazy part is we have looked at the schema, the resolver, and finally, there is like this
glue magic that comes from the Apollo server that basically loads the resolver and then
exposes it in a new URL called GraphQL and that's T that's really literally the only
code that that's something useful in this application.
I mean useful for this example.
And if we now decide as a company it's a great idea to split up the stuff, so we make three
teams.
One team for customers, one for coffee and one for orders and they should do whatever
they want to do whenever they want to do it without interfering with each other.
If you have just one schema, this can be a little bit painful!
Yeah, I think we can tell you in a beer session what we mean by painful.
Yeah.
So good, if we want to split that up, we basically create something like customer in microservices.
Which is just a folder.
And in there, we do three things, we create an index.js -- oops index.js.
Yeah, typing is the most difficult thing.
Resolvers.js and I really understand what the other speaker yesterday meant with it's
hard to type here.
Good, so we have those three things.
I go to the existing schema and basically just copy everything, and remove the stuff
I'm not needing.
Because we're dealing with a customer here now, I just want to query customer.
I keep the type of customer and delete everything else.
save.
I do the same for the resolvers.
Keep the customer data or database actually, because that's what it is.
And remove everything that we don't need.
So that's it.
As you already saw here, this is a function I wrote before, which is just the same, Apollo
server creation that we saw before, this part here with two exceptions.
We built the schema first as a build federated schema and used that schema then to start
Apollo server, that's everything.
That's the only thing that changed here.
Not true.
There's one more thing.
I pass in a path because we need to expose this new schema somewhere in this example.
You would kind of need that, but if you really have different codebases, then every codebase
would have a different thing.
Here I just used one server.
So the path is here.
So we need the index file and I'm lazy because I can't remember this stuff, or actually I
tried to remember it before, but failed like three times, so I just copied this over here.
So OK, this function basically is just a little bit of glue code for my micro-service of the
customers, which just initializes the create Apollo server code that we had before and
exposes that.
So no magic here.
So this is the index that we had before.
We can now get rid of the initial full single schema GraphQL stuff and we just do two things,
we import the Apollo gateway, and we import the function that I just showed you with initializing
the customers.
Then I need to define the URL.
Oops, and not just a URL, but I also need to do just this here, define a URL, which
is GMS, which is GraphQL microservices/customer and I call in it customers, which is exactly
the method that starts up that specific GraphQL server for the customer, and then I have the
Apollo gateway where I pass in this URL of the customer to basically aggregate that.
So we have two things now, we have the separate GMS/customer GraphQL endpoint that works as
a regular GraphQL enter point and now we have the gateway, which is just /graphQL which
aggregates all the other ones, but we just have the customer one and that's it.
And if everything works -- wow, it does!
We can go here and one thing, we can go into GMS customer, and we fail.
Which is not surprising.
Does the regular one work?
No, everything breaks.
Awesome!
Why -- init customers is not loaded.
OK, saving things helps, yes, it really does, OK, that was simple, helpful.
So now we're in GMS customer and we can -- oh, cool, query customers, what a surprise!
So damn it!
Wrong key.
So and here's my point, we can query the customers.
Woohoo!!
And also, we can query the customer here.
Customers and name.
So it's there.
Wow!
Two end points, one is the gateway, this is the gateway here now, and that one is basically
the GMS, the micro-service endpoint.
I'm going to copy over now from a working example the same thing for orders and customers.
Whoopsie.
So you just have to believe me that I'm doing the same thing as before, so we can go there.
Here we got all coffees, and the definition of the schema.
So we just have to import the same thing here, coffee will path, more coffee, and expose
it through the gateway, in the last step, and see if that works.
It does.
So the GMS customer still works and we just on this -- try to see, oh, we have coffees
now.
Hey, cool!
And we just select the price to see a different -- it works.
So same thing, and finally, I already copied it, we do the exact same thing for the orders,
and disclaimer: This is going to break.
Why is it going to break?
Because it complains about customer ID is marked as external, but it's not used.
OK, simple, because what I didn't do before is in the customers, when I copied it over,
I missed one thing.
I basically need to say that it's a key, and I already have done that in coffee, so @key
fields ID, which is this -- which tells the gateway, hey, this is a thing you can reference,
and as soon as I add that, we should be back in business.
And we are.
And it works.
And so now we can try to also select the orders.
And the order ID.
Oops, let's remove these here, so we can see better.
We removed the order ID.
It's there.
What a surprise.
And we add the coffee, name, also there works nicely, and now we add the customer, and it
will break.
Haha.
Plan for that.
OK, so why?
Because we missed the other part, and we take a look at the resolver, and in the coffee
resolver, we already have it, and now I'm putting it over.
This is the second piece you need besides the key for reference is the way to tell the
local service that if some other service comes along and asks you for a customer in this
case, how to do that, and it's -- the underscore-underscore resolve reference method you need to rely
on for that type and voila, it works.
That's it.
Almost in time.
David: So really in?
It breaks at the end?
Nice.
Basically just final words.
Maybe I just put together like a short list which basically some advanced -- more advanced
features you can do with this Apollo Federation, so for example, you can not only like go 0
for one primary key, but you can do several, like you can even do like composite primary
keys, so like a combined key has to be there and so on and so forth.
If you're really interested in all that stuff and want to go into more detail, we get some
references here for you.
Basically the first one is the blogpost published in Apollo, which this talk is based on.
Oops there's a typo and the Apollo documentation and if you are interested in the code, there's
also the link to repository for this demo we just showed you.
So if you want to go into like a simple example and have to code right away, that would be
it.
So at that point, we're done.
Thank you for listening, and yeah.
Gerwin: If you got any questions, find us.
We will talk for beer! [applause]
David: Just to make my mom proud, I have to do one.
Perfect.