Strawberry: a dataclasses inspired approach to GraphQL

Over the past few years, GraphQL has gained much traction, especially in the JavaScript world. Python is getting on board this trend with new interesting libraries. In this talk, we will see how Strawberry makes uses of dataclasses and type hints to easily create GraphQL APIs.

There’s going to be a recap of the current GraphQL libraries available for Python, what are pro and cons and which one is the best for Django. Then there’s going to be a deep dive of Strawberry, a new GraphQL library that makes uses of dataclasses and type hints.

This session took place in track PyConDE and was classified suitable for some domain / basic python by the speaker.

Transcript (auto)

Auto-generated from the recording utilizing Open-Source AI. Speaker labels (Speaker 1, Speaker 2) reflect diarization, not identity. Timestamps refer to the recording.

Speaker 1 [00:02]

Yes, that should be working fine. Hello, my name is Patrick, as I mentioned. I'm a backend engineer at Poland in London, and we do stuff around festivals, travels and events, so if you're interested about that, we can chat later. But yeah, today I'm going to introduce you to Strawberry, which is a Python library that can be used to create GraphQL APIs, and it's something I've been working on over the past eight months or so. But first, let me introduce what GraphQL is, I think it's quite a new thing still in Python. So over the past 10 or more years, we've seen the web evolving a lot. So instead of having a collection of static documents, we now have a proper application running in the browser. In addition to that, we also have applications running on mobile phones, tablets, and other devices. I don't necessarily use web technologies. So our previous approach of having web pages rendered on the server wasn't really working, since we have different clients and different requirements. Just to recap, this is something that we used to have previously. So we had the server that was returning a bunch of documents and maybe some static files, like CSS and images. But then we started building applications even on the web. So we started building APIs. And this could be an example of an application that we have. So we have a bunch of data sources, like database, third-party service, maybe some caching, and then we have a bunch of clients that need to talk with these services. So we start building an API that's gonna connect them together. And the most appropriate way of doing this is REST even today. REST is a simple design architecture where you use HTTP and JSON to extend data between one or more backends and one or more clients. So let's see an example. Let's say that we're building a conference website. It's actually a real things that we are building now. Let's say that we are building this in a way to have just a static website that's talking with an API. So we have a bunch of HTML documents that are talking with this API using JavaScript. And in a proper REST endpoints, we have different endpoints. So let's say that we want to fetch the conference information, so the title and the conference description. To do so, we need to call this endpoint, slash conference slash pycon11, for example. But then we have a list of events and a list of FAQs and in a proper REST API this would mean other two requests. So one to get the events and one to get the FAQs. And REST is a really nice design pattern. I really like the idea of using endpoints and HTTP verbs to do operations on them. But it doesn't really scale up well over time, especially if you have APIs that are quite complex with a lot of resources or have different clients that need the API in different ways. Some of the problems of REST, especially when you compare it to GraphQL, are overfetching and underfetching. Underfetching refers to not having enough data when using API, which leads to do more API calls to display all the information that you have on one page. So, for example, on our previous page, we need to do three API calls to fetch all the information, which is a waste of bandwidth for our users, But it's also a waste of time on our end having to orchestrate all the information and show the page when everything has been loaded. So, just to recap, we have an endpoint that returns the conference information, but if we need to show the events, we need to do another API call to get them. On the other spectrum, we have overfetching, which means getting more data than what we actually need. need. So let's say we're building a welcome screen that says welcome to Parc in Italy. And we're gonna use the same endpoint that we had before, which makes sense because it's returning the information that we need. The problem is that it's returning way too much information. It's returning information that we are not really using while we care about just the title, for example. The endpoint works great, but we just need a few fields. And it might seem a small problem, especially if you have just one client. But if you start having a lot of users or a number of different clients, this is mattering quite a lot, because you're basically wasting data and CPU usage of your users. But also if you have an endpoint that's doing a lot of database calls, this might be quite intensive, especially if you're using just one field from the API. And there are some workarounds. The common one is use get parameters to change the shape of the response so for example in this case we can say i wanna just a field title and that's gonna be returned by the api or you can create custom specialized endpoints for example on page you can create an endpoint that returns data based on what the client needs but it looks ugly it's not really great i think especially because you have to document it and if you start having an api that's quite complex and you have a lot of endpoints. If you start adding different ways of fetching data or if you start creating specific endpoints for the views, it gets really messy quite quickly. And there are some tools that help with documentation but still are a pain. I remember doing documentation on a project that was REST API and it was really painful. So we started looking for some alternatives and one of those alternatives is GraphQL, which has been created to solve this issue and others. So instead of having different endpoints, you have only one endpoint where you can send a document and declare the data that you want. And the server is going to return back that data to you. So let's see an example. For our previous home page, instead of doing three requests, we only do one, specifying the type of operation we want to do. For example, in this case we want to do a query, we give a name to this operation, and we specify all the fields we want to get. So we want to get the conference with id python11, and then we'll get title and description, events with the title, and FAQs with question and answer. So instead of doing multiple requests we only do one for the data we need. So our previous query, when sent it to the server, is going to return something like that. So it's exactly what we asked for, nothing more nothing less. And what's really cool that if I change this request, the data is going to change as well. So that makes really really easy to use this API for different views and different purposes. And additional to that, I think the best feature of GraphQL is that every API has a strongly typed schema. So for our previous API we can have a schema that looks like this. We have a type query which is the root of the our API, so it's where all the fields come from. And we have a conference field which is accepting a code of type ID, and then it's returning a conference. Conference has a title, string, description, string, FAQs and events, and then events have a title and image, FAQs, question and answer. And what does this mean for us? It means that documentation is automatically generated, because we don't have to describe all the fields that we have, everything is already there for us. We can add some description to the fields, but we don't really have to spend a lot of time and describing all the things we have in this API. And then we can use tools like graphical where you can fetch all the information about this API but you can also test things and it's gonna autocomplete when you start typing which is really cool. And there are other tools that make use of typing. I think those are really nice to use features but the best thing is to have for example CI integration where you can check if the fields that you're on the front they actually exist on the API. So I mentioned operations before and with REST you do HTTP verbs to do different things. So get to fetch data, delete to delete, post to create and so on. In GraphQL use operation to express different concepts. So you have query to fetch the data. So this is the query we've seen before. We get a conference with that ID and then we'll returning the title. And then this is what the backend is going to return. And we have mutation to change data. So this is a way to do basically all the operations that have side effects, like renaming something, deleting something, creating a new user and so on. And for example, we can have a mutation that's renamed conference. We pass an ID, the new title, and then we get back the title. And this works the same as a query. This is where it will return. So you don't really have to learn different things to do different operations. So how does GraphQL work on the dual? As I mentioned there is only one endpoint where you send a document using a post request and then when you send this document the server is going to do three operations. The first one is parsing the document and go party to AST. Ransom validation, so check if the requested field actually exists on the API, if the arguments that you've passed to the fields are actually correct then it's executing the query and execution of the query means that it's calling the resolvers that are attached to fields and a resolver it's basically it's a function that's fetching data for a specific field this might be a bit abstract but this is what it looks like so we have type query with a conference field and the resolver for conference fields is basically a function to get exactly the same parameter and then it's returning something this is nothing more than that. GraphQL in a way is kind of like RPC, where you call functions on a server, which is really cool. And maybe at this point you want to try GraphQL. And to be fair, I'm quite happy that things have changed recently in Python, because we, until a year ago, used to have only one library, which is Graphene, but now we have Bufford. So there's Strawberry and then Ariadne and Tastyflat, which are quite new as well. And it's really good to see that the ecosystem is improving. But yeah, today I'm going to introduce Strawberry, which is the one I've been working on. I used to contribute to Graphene, but then I decided to make something else just to scratch my own itch. But before going deep into how Strawberry works, I'm just going to introduce a couple of features of Python 3 that we use in Strawberry. The first one is type-ins, and type-ins are a way for developers to give annotation to a code so you can document what the code is expecting and what the code is returning. Let's see an example. This is a class user where you have a constructor accept a name of type string and an age of type integer. And then we have an evaluate user function that's accepting to user. And type ins can be used for... well I should mention that I'm not going to use this. This is valid Python code, but Python is not really caring about the types, this is just for readers of the code and maybe for some tools. So they can be used for documentation. So you can understand easily what a function is expecting, what is it returning, and you can also run tools like MyPy to do static analysis of the code. So you can catch places where, for example, you're using the wrong types, or maybe you misspelled something. With modern ideas, you can also get autocompletion, which is quite cool, it's quite useful. As I mentioned, type-ins are not used at runtime, but there are some libraries that make use of those even at runtime. For example, there's a library called Enforce that's going to enforce the type at runtime, which is kind of useful in some cases. For example, if you're talking to a third-party API, you can make sure that the data that you send is actually correct. But there's also another Python library that makes use of type-ins, and that library is called data classes, which is built into Python 3.7. And this library simplifies the process of writing classes that acts as data containers. So for example, our previous user class could be written like this. So a class user with two fields, one is name and one is age, accepting name is a string, age is an integer. And what data class is doing is basically a bunch of magic methods for us. so it's creating the constructor representation method equals less than greater than and more you can also become configured to to change some of those uh those methods and this is really cool because um we start using data classes that work quite a lot um i think they help uh us to think how you structure data on your back and also are quite useful when you talk with third-party apis So for example, instead of us sending dictionaries around, we use data classes so we're documenting all the parameters that we're passing to a third-party API. But what about GraphQL? Strawberry is using the same approach as data classes. If you think about it, GraphQL types are data containers anyway, so we have a conference type which is just a bunch of fields with types associated to it. It's nothing more than that. So in strawberry we use exactly the same approach as data classes. We have a decorator that's going to do a bunch of things on this class. Basically, Sorbet is going to generate the GraphQL types using type-ins. So it's going to fetch all the fields, we're going to see the types, and then it's going to create the GraphQL types that we can use for our API. But what about resolver? So if you've seen this, it's cool, it's easy to use, but how can I fetch data for this? So we have a way to do so, And this is the one of the... We actually have two ways, but this is the easiest one, where you can pass a strawberry field and you can pass the resolver. And when you ask for that field on the API, that function is going to be called for you. So let's build a simple hello world. This is the simplest hello world you can do with strawberry. Just a bunch of lines that you have a proper GraphQL API that's running in the... that's running. So in this case, we have a query that has only one field called hello which is a type string and returns hello world and this is also to show that you don't really have to write resolvers there's always a default resolver attached to the fields and it's going to return the current value of the field then you can generate the schema using server schema and then this can be used with the debug server and you have an api that's up and running for you but yeah let's make this a bit more dynamic. So let's say that we want to return a random greeting instead of just a hello world. First thing we have to do, we have to create a function called resolveHello. We have two parameters. Root is all the data that comes before this field. So in this case it's going to be nothing because there is nothing above hello. And then info is just a bunch of information about the the request. So hook and sample allows you to get the current user, headers and so on. And then it's on a string and then it's returning a random choice. Then we have to change the the field to specify the resolver. And actually I have a demo here. Okay, this is a bit more complex than what I showed. But yeah, we have a bunch of types. So we have a person that's first name, last name, age, and then also has a field called full name. We have a company type that's a namespace. We have that database of people. Then we have a bunch of resolver for... This one returns always the same person. This one returns a person by id. And then we have a query where we define person that's going to use the resolver person. And then we have person by id. And we also have company. And we also have a mutation but maybe I'll skip that for now. So to run this we use the... I'm using pipenv, but I've worked with anything. So you do serveryserver, the schema file, and then you get an API that's up and running and you can use. Okay, cool. So this is a query I can do. So when I get the person and the first name, when I call it's gonna return the first name. But if I want to get, for example, the age, I can do so. If I can add a full name, that works as well. And what I was mentioning before, that you can introspect the schema, so you can see all the fields that you have here. So you don't have to, for example, if you give me this API at the front-end person, they don't really have to see the Python code to understand all the fields that you have. They can check the schema. But they can also check the documentation, which is a bit easier. You can also filter things. And for example, we have a person here. You can add some description then you can understand what all the information are in this thing so let's see maybe let's see the other one so person by the you can pass an id and then this is going to return that person it should be okay yeah i think yeah this is basically a natural it's really really cool and easy to use especially for front-end people which to be honest is quite exciting because you don't have to deal with them asking you, oh, how can I fetch this information? That's cool. Oh yeah, I think I deleted one slide. Anyway, we have some other bunch of features that I think make Strawberry Hub be more interesting than other libraries. The first one is something I really like. It's called permission classes and it's inspired by Django REST framework. so you can pass a permission, a list of classes for permissions for a specific field so you can make a field that's generic enough but also doesn't return information to the wrong people. So for example you have a user type where the email is only returned if the current user is an admin so you're not leaking data to someone else. We also support ASCII. ASCII is the asynchronous version of WSGI, so you can use Storeberry with servers like UVCore or Starlet, and you can build the API and make it up and running quite quickly. We also support Django. We currently have a view that you can use and you can import into Django and use to render the graphical interface I showed before, but we're also working on a conversion of Django model to graphical type, so you can convert the model to a graphical type without having to define all the fields again, kind of like Jungler's framework. Yeah, but not everything is cool. Currently, Storber is quite unstable because we have been focusing on having an API that's really easy to use. So we're trying to shift things and break it, at least for now, unless we get to like a stable release. We're also trying to experiment with cool features. And my next focus is gonna be, apart from fixing a couple of bugs that I found recently. It's to work on the new website and documentation, because if you go to the website, there's just a bunch of text that's not really telling anything, and there's no documentation right now, which is something that, for a library, is a bit annoying. But we are looking for a contributor, and this month is Oktoberfest, so if you send a pull request, you can get a t-shirt from Oktoberfest, but you can also get a sticker from us. I have some stickers here if you want, anyway. But yeah, if you send up a request, it's better. But yeah, to be fair, it doesn't really matter if you use Strawberry or any of these other libraries. I really want to see people using RQL in Python because I think Python is definitely the best language for doing backend things. So if we start improving this technology, I think it's quite amazing. It's quite useful. If we start using this technology more in Python, we're going to improve the ecosystem quite a lot. And people are taking pictures. Can I keep this slide? But yeah, what I was going to say, I think GraphQL improves both the developer experience, especially on the clients, both on the backend, and it's also going to improve the user experience for the user. So I think we should probably use this. So if you have any questions, I'm happy to take them and thank you yeah thanks very much for the talk we have a lot of time for questions so I'll run around and see if there's any so here's the first yeah hi thanks a lot for GraphQL also a huge GraphQL fan and we're enjoying that it's right now gathering in Python some attention and And my question is, so n plus 1, do you somehow try to fix this issue with GraphQL? Do you have some plans? I don't think I'm going to have plans, at least for now, in Survery. Because there are already some solutions that you can use. So maybe it's easier if I go here. So one solution is to use the info field to... This is going to hold the old GraphQL request. so you can check all the fields they're requesting. So it's easy to, for example, if using Django to do select related, so perfect related. That's one way of doing it. There is another option that's called data loaders, which I never tried to be honest. That would be another solution that you could try. Actually, funny enough, I had a blog post open here. It's probably here. Oh yeah, but yeah, so I need to read about that. But yeah, we don't really have plans for solving that, at least for now. I know there are some, like, for example, for Graphene, there is a Django optimizer that's going to introspect the request. This is something that maybe we can try and support as well, but not for now. All right. Yes. Yeah, hi. Could you tell us a bit more about performance and speed in comparison to standard REST API approach? That's a very good question. It really depends on what you're doing. So let's say that you have an API, like a GraphQL API that's quite optimized. So you're doing optimization also on the database level. So for example, let's say we have the conference endpoint, sorry, conference field in GraphQL and you're only fetching the title. What you could do, you can introspect the request and you can also tell database to just return that specific field. So that's making a bit, it should make it faster because you're just returning less data. But you can also add caching on top. I think one of the issues of GraphQL is that using post requests. So, that makes it really hard to cache it because you cannot use tools like Varnish or you cannot really leverage client caching that easily. There are some solutions of that. Some of them are a bit complex. Like, for example, there's a way, it's called static queries. So, you basically, you have a frontend. You compile the queries to, you fetch all the queries that you have. Compile them to an ID, an hash, and then you send a GET request to your GraphQL API, and then the backend is going to fetch the query associated with that ID that you're passing. And that allows you to just use GET requests. It's making it faster as well. I think with GraphQL you need to be a bit careful, but you always need to be careful. But I think it's easy to make APIs that are quite slow, because you can fetch everything you want. So for example, if you have an API that's straight converting from a Django model, for example, you can fetch all the related fields and you can go really deep and that's going to make it slow. So yeah, depends on use cases. There are some things you can do to prevent that. We can chat later. Thanks very much. Thanks. What does using this with the async frameworks look like? Is it as simple as declaring the resolvers as being asynchronous yes that's the slider deleted for some reason let's see if I have it oh yeah that was the one yeah deleted by mistake yeah you just put a thing in there and works to be honest there is a bug in the select in the ASCII app which I'm gonna fix the next couple of days which I think it makes this not work but when that's fixed it's kind of work. So, you just use async things. I think an issue that I had, I was using, like, here I'm using requests, but now there is HTTPX, which is an alternative request. And that doesn't work for some reason because it's creating its own loop, asyncIO loop. So, I need to check if that's a bug of Strawberry or if it's a bug of HTTPX. But yes, with request works fine. How tight is this thing depending on Django, and is it possible or maybe you have plans to adjust it to Flask? Oh yeah, that's the same question. So something I should probably mention, GraphQL doesn't really depend on anything. So you can use it with different databases, you can use it with even REST API. So some of our work, for example, we have a GraphQL layer that's talking REST APIs. But for having a view, like for example Django one, we don't really have built-in support for Flask at the moment. It's something we want to introduce, but you can use, there is a third-party package called GraphQL Flask, I think, that you can pass the schema that you generate by doing this one, the schema at the bottom. So you can pass that to the Flask GraphQL app and it works. Okay, let's see. Is there any more questions? Okay, if we don't have any more questions, let's thank Patrick again. Oh, is there a question? There was one. Oh, sorry, I can't. I don't see. Oh, here you go. Hey, thanks a lot. I was really excited to see GraphQL in production properly someday. And regarding to that, do you know if there is any strategies how you can gradually maybe introduce GraphQL into an existing project? Like, if there is anything that helps you, like, get started? Yes, it depends what you have already. So, if you have REST APIs, something you could do, you can have an API gateway in front of the REST API. So, you have this gateway that instead of being attached directly to the backend, it's going to talk with the REST APIs. So, when you build new things on the front end, you can start using GraphQL for the new things. And you can reuse your REST APIs, which I think is quite cool, because you don't really get the benefits on the backend, because you still have REST APIs, but on the frontend you get a lot of benefits, because you can fetch the fields that you want, you can leverage. On the frontend there are so many frameworks that are quite useful, and they do some caching and data fetching really quite smart. Some other people in the JavaScript world, they do the GraphQL gateway on the frontend as well, which is a way of doing it but I don't really like it Okay, we have time for one or two more questions if there are any So, since we have any since we don't have any more takers let's thank you again for the talk

Patrick Arminio

Chair of Python Italia. Trying to make GraphQL in Python more accessible and known

Social card for talk: Strawberry: a dataclasses inspired approach to GraphQL