 All right, hello, Ron, for that very last session on that nice conference. Thank you for that kind of introduction. So you most likely, oh, sorry, it doesn't switch slides. Okay, so that's what you've heard already. So my name's Arthur and I'm a Python engineer on my daily basis. And today I would like to talk to you about some experiences that I gathered during quite a large project that I was developing along with my team. And how we have created GraphQL umbrella, or API gateway for our project, which was consisting of a lot of different microservices. So let's move on. At first, microservices. Please raise your hand if you ever have used microservices in your project. Whoa, that's near full house. And now, please keep raise your hand if you always implemented those microservices by the book. So you kept all those responsibilities divided. You always used loose coupling and you never did anything wrong. All right, nobody, myself included. So what happens if you don't follow those guidelines, those rules that our industry advises to use? Well, you accumulate tech debt most likely and you end up in not very nice place because you need to always deal with all those trade-offs. Quality versus time versus effort put into developing certain feature. And that also means that distributed systems are getting complex over time. And instead of having a nice and divided microservice architecture, you ending up in such a mess. So everything is interconnected and everything requires everything else in the system. And yeah, I was also there. And in our project, we had architecture that used to be called the microservices but it has some flaws of a distributed monolith, unfortunately. So what it was, it was a basic or maybe not that basic, but e-commerce platform that was essentially selling staffs or taking orders from the customers, doing some background processing and forwarding them to the suppliers to fulfill. And we were been doing that using almost 15 plus microservices written in various technologies. So more modern, we faced API, some older ones with Django and there was even some components that were written tornado or plain async IO. So quite a lot of different stuff. So let's draw it at first. We had an Alf service, which was responsible for authorizing users. We had the order service, which was taking orders. Product service, which was offering those products to be purchased by our customers. And a few more of different ones that are not that important. So all of those services were exposing PlainRest API and they were communicating mostly over PlainRest API, unfortunately, so that's why they were, it was a distributed monolith, unfortunately, not a microservice architecture. And also they were exchanging some asynchronous messages using RabbitMQ. And up to this point, it's not that bad. Okay, we have a lot of services, but it looks manageable. What's in the other end on the project? So what is the consumer side? On the consumer side, we had a few react-based applications which were communicating over that rest API with all those services. So we had a storefront application, which was the primary point of contact between our customer and the system. We had a back office application where our employees could do some support for the customer so see the specific order statuses and maybe switch them on or push something forward if something doesn't work. And still up to this point, it looks manageable. So what was the goal? The goal of the backend engineering team was to create some more microservice oriented architecture instead of that kind of beast. So we wanted to keep refactoring the services, maybe combine some of those, maybe introduce something new if it was really needed. And on the other side, the front engineers wanted to keep using the API and they really wanted to avoid breaking changes. So why was that problematic? So instead of having only those two applications, we had also a third player in the party, the business. So our business stakeholders were really eager to introduce new checkout system. So they wanted to have extremely new checkout experience which was like natural for our front engineers to create with another front application. And of course, there were some rumors that we are about to go full into the mobile application because everyone likes mobile apps nowadays. So how those frontends were communicating with the backend? So the storefront needed to know how the user or who the user is, right? And also the storefront also needed to display the history of orders once user logged in pretty standard commerce behavior. And finally, the storefront needed also to offer some products. And most likely, you already see where I'm calling to. So all of those services were completely aware how the system is constructed, what are the components and where to look for the data. And it was nearly impossible to refactor that system without introducing breaking changes. And that was a problem that both front engineers and back engineers needed to fix somehow because we needed also to expand, create new features and we couldn't afford spending entire year on refactoring, right? So we sat together and kept thinking how to fix that problem. So at first we thought, okay, there is that nice pattern in the industry called backend for frontend. Okay, those slides are slightly misaligned, sorry. So backend for frontend. And in that approach, storefront application would have its dedicated backend API which is entirely focused on creating the subset of APIs that are specific for the storefront, right? So display list of products, display history of orders and so on. And also back of this API display list of all orders across the system, all users, all products and so on. And that's look kind of promising. However, it's not even remotely close to reducing number of the services in the system. And given our size of the team on the bucket, it was unmanageable to introduce even more to the stack. So we started to think, what are the other approaches? And also it's worth mentioning that in that approach we will have a lot of business logic application because fetching orders will leave both in storefront and in back of this API. So we in the end decided that we would not follow that approach because it was just too lot to carry. So what was the other approach that we have established? Well, instead of having four new API gateways, we decided to have just one. So that's another like a trade-off in the API architecture because we indeed introduced a gigantic single point of failure in our system. However, we decided that this is the trade-off that we would like to move on with because we have nice infrastructure that scales and deal with it with that kind of cases. And within that approach, we will be able to connect all those front applications to the backend without letting all those front apps to know how the data is organized under the hood. And here GraphQL comes quite handy because as you may already know from the previous talk, GraphQL is query language that is of course developed by Facebook and open sourced. But first of all, it's statically typed. And that's a main feature that is really useful, especially when your front-end teams is in love with TypeScript. Because there is a lot of cool tooling around that allows them to just use those static type system. There is other kind of cool feature of GraphQL because it's protocol agnostic, which means that you can essentially format your messages using GraphQL, but you could exchange those messages over any medium you want. Of course, all of us, we are web developers, so we will use HTTP for that. But it doesn't matter that much. You could safely use, for example, RabbitMQ or any other message exchange system just to exchange messages formatted as GraphQL. So how it looks. In GraphQL, we have a thing that is called GraphQL schema, which is essentially a contract between the front-end apps and the backend system. So in that simple example, we have a query that TypeQuery is responsible for outlining what are the read operations from our system. So we are able to fetch a list of orders. And each order is very granularly typed. So we exactly know how the order looks. How it looks on the client side. Why is it even useful? So that schema is especially handy because it's used both on the client side and on the server side. So the client fetches schema that is exposed by the GraphQL server. And before it does any GraphQL query or mutation that mutates the data, it validates locally the data, the query that is about to make to the server. So it could eventually rise in exception if something is not valid without even bothering the server. However, if everything is fine, we call the server and we execute queries and mutations formatted by using the GraphQL schema types to the server. The server also validates if everything is fine according to the contract. And if everything is fine, it returns data to the calling client. And here is also the important part because the data is returned in the same way that it was asked for. So the client essentially outlines in the query what kind of data it's interested in. And the data will be returned in the same form, in the same order, and there won't be more data returned or less. There will be always everything that was requested. So moving on, that's the simple GraphQL schema that would be used in my further examples. So we have a query that is returning orders by giving a user, by taking its order ID. And we have the order type and the user type as well. And here also is a pretty cool feature that is called schema directives. If you attended Patrick's talk, you most likely are very familiar with that. This is a built-in one that allows you to do a safe deprecation in your API structure. So every client that will be using a deprecated field will be notified on that validation level that he is using a deprecated field and the developer should consider refactoring the client. Okay, let's move on. How to use GraphQL in Python, you might ask, since we are on the EuroPython conference. And there are a few approaches, as always. So I believe the most mature one and the oldest one that I can know is Graphin Python. Graphin Python is a library that is a code first library which means that you need to know Python in order to create GraphQL server. So the syntax looks for me pretty familiar to the Django REST framework if you are familiar with that tool. So essentially you are creating classes which contains framework native fields and after processing, those fields are generating the schema which is later on used by the client. The other approach is presented by Strawberry, of course, and in this case, it's also code first approach. However, the code itself is more straightforward and it's focused more on Python native type hinting. However, still, in order to keep the schema up to date to modify it, you need to be a Python developer. And you may ask why that even matters. So this is a third approach, or second, third library that is called Ariadne library. And this is a library that is a schema first GraphQL library, which means that you need to create both. You need to create schema file in the GraphQL files and later on, you need to load those files into your Python code. And from the Python code, you are mapping those schema entries and operations into the Python resolvers. And which one should you use? Schema first or code first? As always, it depends because it really depends on your use case, your project and how are you working with your API. Let me do a quick comparison of all those three tools. So those are looking really bad, sorry. So in schema first, your front engineers could really contribute to the schema development process. So if you attended the previous talks about creating behavior-driven applications, that schema could be your like ubiquitous language between front end and back end engineers because essentially what front end engineering team could ask using the schema, the back end engineers would need to deliver. And there will be no more arguing about why that field is null or not, or it's a list or a single field. In code first, on the other hand, the entire schema development and server development thing is heavily back and focused. And as I mentioned a few times, you need to know Python to develop that code. And also in my opinion, it's hard to predict what kind of schema will be generated because it's really up to the framework because how those fields are mapped to the schema, it's only known if you really know the framework that you are using. In schema first approach, you have full control over how your API is defined and it's really easy to track changes because the schema files are essentially yet under artifact in your code repository. So everything is visible. Unfortunately, there is more code to maintain. So the decision is up to you. In my use case, I followed the schema first approach because I had that first use case here. So the team of front engineers would like to contribute to the API structure. So they was really eager to define what kind of schema they would like to have on the end. And what kind of features were really useful for a GraphQL gateway development? So that was the system that I presented to you already. And here is our query. So why do we even bother to introduce GraphQL there? So in that query, we would like to get list of products ordered by given user. So for each order, we need to display product name and its price at the order time. And that's kind of information that is kept in the order service. And also the designer designed a reorder button. So we need to know if the product is still available. However, the order service is not aware because it's not the context of the order service, right? So in order to deliver such information, we need to contact the product service to resolve the product from that order that we had at hand. And that compose information will be returned to the storefront. So we have broken that tight coupling between orders and products and the storefront. And we abstracted it with a GraphQL gateway. Now only the GraphQL gateway knows how that data is oriented on the backend. And all those things are possible and they are performant enough because the resolvers that are tied into certain fields are fully asynchronous. If you are mapping your field to a resolver, you are able to do any logic you want. So for example, you could do an underlying GraphQL with some asynchronous HTTP library. You could also do a database operation, cache operation, or you could even reformat that data on the fly because the front end could need the data in slightly different format that you keep in our API. And all that things you could do on the resolver level. So is it everything that colorful full of rainbows and unicorns? Well, no, it's not. So there are a few drawbacks and cavies that you need to be aware of before you jump into implementing GraphQL in your system. First of all, error handling, it's completely different from error handling that you know from the safe space of the REST APIs because the data could be returned in parts. So it could happen that you have requested like two fields, for example, orders and products separately. However, for some odd reason, product service is not responding correctly and you could receive only orders or nothing in that scenario. The query was successfully resolved and it was successfully executed and it matches the format. So it's HTTP 200, okay. However, there is no data in the response at all. However, you have pretty for both error message and it's completely up to your client to deal with that problem. Under that link, there is quite extensive information how to create that kind of error handling that is useful. So error handling is tricky for sure. Moving on, the machine to machine communication with all that coolness of GraphQL, unfortunately is, in my opinion, not well suited to do any machine to machine communication mostly because of that problem with error handling and all of its verbosity. Because in scripting and in server to server communication you have to be clear, is that success or not. So in my certain scenario, we kept using REST of course because of a lot of tech debt that we have accumulated but that's a different story and it was just easier to maintain and easier to work with. We kept the GraphQL only on the client facade layer. Moving on, file uploads. Well, with all that static typing, it looks quite tempting to use it also for file uploads, for example, if you have system that would like to keep some files from the user or take some files from the user. Unfortunately, the schema definition language doesn't support binary file. Of course, you could smash it into a base 64 string and just push it forward but it's not the perfect scenario, believe me, binder. So it could cause serious performance issue and I encourage you to use direct cloud uploads to your storage, create some sign URL link from your API and just upload from the client. And last but not least, naming. Naming is difficult because in that schema you have single namespace by default. So if you, for example, name something as a product and later on, for example, you are developing the administrative panel or back office application and you need product but with different set of fields but the product is really taken. So you need to be aware of that issue and be careful how to pick your names. However, there is some quite good material how to format the GraphQL schema in order to at least introduce some sort of namespacing. And also another problem could be an N plus one queries. So you could observe that already if we have that kind of operation that expands product availability and price for every single product in an order. So what happens if my order contains a hundred products? Well, by default, we will do a hundred queries for a single product to the product service and this is not cool. So how to circumvent that problem? And there is a way out of it. So there is a cool pattern that is called the data loader and the data loader will detect if you are creating or if you are resolving certain field out of times because of the asynchronous query execution you could proceed with resolving the entire query tree and the data loader allows you to group those extensive queries and instead of doing a hundred queries for a single product, you could do one query for a hundred of products and keep your performance on an acceptable level. And that's because of the data loaders. To wrap up, if you are using microservices you should really consider using API gateway if you don't have one. And if your use case allows that you could also consider using GraphQL to do that. Thank you. Thank you, Arthur. Do we have any questions from the audience? Thank you for the talk. I have the following question. Do you have any tips and tricks? How to analyze what browser sends to GraphQL because, for example, frontend very often sends tons of requests and all of them was a follow-in network tab. Yes. How do you work with that? If you use Chrome, for example, there is that cool extension to Chrome DevTools that is called GraphQL network. And it will detect and unwrap all the GraphQL queries in another tab in the network tools. Thank you. Anyone else? Thank you again, Arthur. Thank you. Thanks for having me. Thank you.