Building high-performance, type-safe GraphQL APIs with Strawberry and Django Thiago Bellini Ribeiro
Published November 22, 2023
This video features Thiago Bellini Ribeiro at DjangoCon Europe 2024 in Vigo, Spain.
Workshop: Building high-performance, type-safe GraphQL APIs with Strawberry and Django by Thiago Bellini Ribeiro
https://pretalx.evolutio.pt/djangocon-europe-2024/talk/WN3GGN/
Strawberry lets Django developers define type-safe GraphQL schemas with Python type annotations and dataclasses, exposing exactly the fields clients request while supporting relations, documentation, deprecation, and schema evolution without versioned APIs. Strawberry Django links GraphQL types to Django models, automatically maps fields and relations, generates resolvers, and supports filtering, ordering, pagination, mutations, and async execution. The speaker emphasizes that GraphQL’s flexibility creates security and performance risks, then shows how field extensions and permissions restrict access, while the query optimizer uses `only`, `select_related`, and `prefetch_related` based on each query to avoid N+1 queries and unnecessary database work. They also cover optimization hints, data loaders for more complex cases, recursion-depth limits, custom field mappings, and how mutations and validation work.
Summarised automatically from the transcript.
Automatically transcribed, so expect mistakes in names and technical terms.
Speaker 1: um your schema so when you say that you are returning a type that has uh a string and an integer in that and uh in in some attributes you are required to return as that and the graphical enforces that uh no equality redundancy because as for the same reason as the first uh uh topic the first reason You have no issues with overfetching or underfetching, so you don't need to build a lot of endpoints to serve different datas You can have one in the client who choose, oh, I want just the ID and then the name to do a listing, or I want a lot of other stuff to show a detail stage. And is access to relations, which
Speaker 1: we'll get more clear when we see the next slides in some examples And no API versioning required because you can when you build a GraphQL type, you can deprecate any types and any fields in that and mark those as deprecated So after that you have some time to actually plan your the removal of that field and make your API evolve without having to do v1, v2, v44, and so on. But of course, there are some cons. There are some potential security concerns if your relations are not resolved correctly. We're going to talk more about that. soon.
Speaker 1: Also some performance issues related to relations as well, which we are going to cover those two topics really soon. And caching is more complicated because when you do a get request, uh the browser can uh cache that request for you. For GraphQL, all requests are posed by default. So there is no way for the browser itself to cache. There are other alternatives, but we're not going to go into details in this presentation. But yeah, don't feel you don't have to fear the cons because strawberry got you covered. That's the whole reason I am doing this presentation today. So let's show some examples first about the pros because let's talk about the good stuff first.
Speaker 1: So no overfetching and no underfetching, what does that mean? Suppose that we are building this uh this request. This is something something that we are going to uh send in the body of the HTTP request. In here we are asking for the user uh that goes for the primary key one two three And we are asking it to return its IG username and first name. The response should look something like this. So we get exactly what we asked it for, the user with the IG username and first name We can change that. Supposedly the this type, the user, exposes more fields, so we can add ask for more fields from it. In this case, we remove it actually, the idea as you can see.
Speaker 1: But we are all now asking for the last ID and the list of emails from the user. And you can see that now the response looks different. So we get exactly what we asked it for. Type safe and auto-generated documentation, what does that mean? This is a typical GraphQL type that you define inside your schema. And here we are defining a user and the emails. As you can see, the user has as we requested the username, first name, last name, a list of emails, as you can see here, and the email type itself. So and when you look at the schema, you know what the user can provide to you, you know what the email can provide to you, and you also have even
Speaker 1: the option to document it field, similarly to how Python DocStrings works. Uh the schema does basically the same. And is that access to relations? Let's look at the example again. Uh If I access an uh if I ask for a user, it's easy to transverse to the emails, and also if I ask for the emails, it's easy to go back to the user. And so on. You are building a graph, so in theory, from one point you can go to any place in your graph as long as there is a path that leads to that other data. An example of that, for example, in here again looking again at the same example, we requested the emails in there and it returned the emails in there even though it is not the same.
Speaker 1: If we talk databases, it's probably not coming from the same table. The user is one table, the list of emails is yet another table that has a foreign key to the user. And no API versioning. So let's take a look at uh yet another example. Uh suppose that in this case we have this user type. We and we want to change it instead of returning first name and last name because well the front end maybe is not uh don't doesn't want to have to deal with it, it just wants to show the name directly. Instead, we want to expose the name. And email was a string before because we were using email from Django, for example, that comes built in
Speaker 1: the user model. but we want to convert it to a um a a one-to-many relation with uh in a mail table. And how can you do that? And we don't instead of doing v2, v3, what we would usually do on our S CPI, We can basically mark those fields as deprecated and create the new fields. So our type will have all the fields at the same time for same time, but After a while, uh this will give use uh warnings to the clients using using those fields, and after a while you can basically okay, those fields I'm going to remove them, they are Deprecated for some months now, no need to have them anymore, and your API evolves as required.
Speaker 1: Uh so that's great. But like I showed some examples, but how can I actually write a GraphQL API? So let's start from the beginning. What you need to do? So let's define our user type. The same as the example before. I'm going to use the same example as to avoid uh confusing presenting some other stuff. But in here we have the user with a username, name, birth date, age, and emails. Let's also define the email type that you already saw before. It has a foreign key a relation to the user. The email itself, which is a string, and a Boolean marking if it is the primary email for the user or not.
Speaker 1: With that, how do we actually retrieve that data? We need to define something in our query, which is basically an special type that defines the root of our queries. When we request data, the root uh attributes that we can access is what is defined inside the query. So as you can see here we we have uh a me uh a me um attribute which should return a user supposedly it should return like the currently logged logged in user A user that can receive an argument primary key PK and will return a user, and also a list of users with pagination capabilities.
Speaker 1: So just to exemplify, let me show a quick demo here. Hopefully, during the end of the presentation, I'll be able to show some more. examples in here but this is uh just to um do a quick demo about what I've showed so far So in here I have this playground, which is basically a tool similar to OpenPI Swagger, which you can use to send vertical queries and get the results back and see what they look like. I have it running in my I have it uh it's a Django project running the background. So in this example I am asking for a user that goes with the PK1 and ask for its
Speaker 1: ID and name And when I do the request, this is what I get back. If I try another user, for example, I'll get the same the same data schema but different results Um I already had had this prepared. So in here it is it is the same as you can see user. It is the same as this. I'm just using a variable here to show that you can actually have this and pass those as variables so those can be reused. And in here I'm asking for the age, the ID, the age, the first name I deprecated that field. So it's that's the reason it's showing like this. The name, the emails, etc. And if I ask for the user one
Speaker 1: again, you can see that it's the same data as user one in here. It is the same data, but now it has more fields. If I go to user 2 , it's the same as well. So going back to the presentation. Okay. Okay, now it's working. So uh but you might be thinking now okay but that that doesn't make sense. Uh you showed a lot of GraphQL uh queries schemas, but how how can we actually do that in Python? That's not why we are here uh to learn about so yeah let's let's do that uh so how uh so going through the beginning
Speaker 1: I said that the first thing that you need to do is to define your types so let's look again at our types how can we do that in python Anyone that is familiarized with data classes Pydenk will know what is going on here, but uh we It is very easy to see that we can represent those types as those data classes. We have the user data class in here, the email, the string is converted to an Str. The other string here is converted to an SR or none. You might be thinking why or none? Because this one has an exclamation point, which means it can't be known So this one by default can be no so or none and so on. And how
Speaker 1: can I use that inside my strawberry project? You just change data classes to Strawberry. type. What is happening there is that Strawberry. type is basically a wrapper on top of data classes that enhances it and uh Tells the schema what the schema is and uh does some other magic that happens behind the scenes. So let's define now the now that we have our types defined. Let's start defining our root queries. So let's start with the the me field. The query is basically a type as I showed to you before.
Speaker 1: So we can basically do the same. Sorry. Uh we can do the same. Um uh decorate uh a class with strawberry dot type And type me as user or none because rod if the user is not logged in that should return new But now okay, so we might be thinking okay GraphCal now knows that me returns a user are known, but uh how does uh but how does it know how to fetch data from it And the answer is it doesn't know. We need to tell it how to fetch the data. So that is where uh resolver comes in. In this case, we are still typing the me
Speaker 1: as user on one, but we are now assigning it to strawberry. field, which is also similar to data classes. field. And passing a resolver to it. So anytime that we try to resolve a me field, uh that uh the get user function is going to be returned to the uh is going to be called And its return is going to be returned inside that field. Another approach, and the one that I actually prefer to use. is to use the strawberry. field as a decorator instead of doing it like that because then your code gets more is more contained inside the the the code inside the type itself and makes it it is more legible, at least in my opinion.
Speaker 1: Um and also um going now to the user that receives a PK argument You can see that it receives an argument, so but uh how can we tell that in Python? As simple as testing an argument to our resolver function and typing it So strawberry is going to uh introspect our function and use its return value and also the It's return type, I'm sorry, and also the type annotations in your arguments should to generate the the final graphQL schema The same for the users here, going really quickly because it's basically the same as the example before.
Speaker 1: We converted the arguments dimension of set to Python arguments. The only difference in here is that we are returning a list of users instead of a single user So uh in the end, um I was I went step by step in that, but in the end that is what our final query is going to look like in Python uh with the three dots in there because uh I can't fit all the code together in there. Uh but now let's dive into uh uh Django itself How does that all connect to Django? So let's use this model as an example. The same as we had before.
Speaker 1: So we have this model in Django user. First name, last name, birth date, all using sharp field sharp fields and date and date fields. Um how can we return it uh in our API? So using what we learned so far, you probably are already guessing uh how we can do that. And yeah, the basic the uh the more the most basic way of doing that is basically defining your type and defining uh a strawberry field and querying for the user uh in here and instantiating the user type itself with the the I need to stop moving my mouse. and uh and uh instantly
Speaker 1: the user type itself with the um the data from the model. Um The email. Oh, we can do the same for the email. I think there is an oh I'm sorry, I got confused with another slide. So um we have also have the email model in our database. And we can of course do the same with it. In this case, the only difference is that the user was a root query, so we would we wanted to return it inside the query type itself Now uh the email is a subfield of the user type. So uh we can also use the strawberry field inside any
Speaker 1: types Be it the query type or in this case the user type, and the resolver is going to do it exact exactly what we expect it to do. It will uh do an up uh email dot objects. filter to filter by the user that goes. uh by the PK inside this user type and return a list of the email email types instantiated. But um you might be thinking okay that seems stable right uh there maybe is there a boiler a way to reduce that boilerplate code because we are basically just uh doing what REST would be doing So uh strawberry jingle comes through the rescue now, which is the the integ uh the integration itself
Speaker 1: What we need to do, let's take again our user type. Instead of using uh strawberry dot strawberry. type We can now use strawberry django. type and pass the user model to it. What that will do? That will basically link the user type itself with the user model And use that to do some um magic behind the scenes. The first one is basically that. So we uh note that we are now typing the model we can still type the other way just to make this uh clear but we can now start using strawberry. auto to type our fields What will happen behind the scenes is that
Speaker 1: the integration will introspect the our model that is linked and you'll say okay, so I have a name strawberry auto uh it will go to the to the model and see oh it's a sharp field so since it is a sharp field I will type it as a string. Or if it is a share field that can be null, it will type it as an STR or none, and so on. The second thing that the integration is going to provide is um okay, and just another example about the the auto. Oh no, this is actually the the what the I was going to uh to to say another thing that the integration is going to uh to provide is automatic
Speaker 1: uh automatic resolver generations So in this case, remember that we had this boilerplate code to return the users. Now we can just say that emails is a list to email type. Because we have strawberry django. type um and um as an um a decorating the type it will know it knows that emails is actually a related manager to the email type to the email model And it will automatically generate a resolver that will basically do something very similar to what we are seeing on the left side here But you don't actually need to do that yourself. Unless you want, of course, you can always do the manual
Speaker 1: way, but this is something that the integration will provide to allow you to write less boilerplate code. And uh this also applied to root uh queries as well. Remember that we had user and users in here, and we had to def to define a uh A resolver to find the user, another resolver to list the users. Now we can just do uh uh do this. The only the main difference in here is that because query itself is a strawberry type is not a strawberry jangle type We need to assign it to strawberry jangle. field um for the magic to happen. But in the end, what will be happening
Speaker 1: is For the user for example, because it is typed as a single type a single strawberry jangle uh type. It will automatically generate a resolver that receives a PK and returns the model itself, similar to this that we had before. For the users, the only difference is that because the user, because the annotation is using a list , It will instead generate a query that returns a list as well. There are built-in options to add filtering ordering pagination and also There are some security stuff that we're going to talk more
Speaker 1: in a bit. But that that is what the integration is going to do automatically for you. Okay, so that's great. How about but what about the concept? Exactly what I was talking about. Maybe you already are concerned about some of the stuff that I showed here. So let's remember those. So uh the the main ones that I listed was the potential security concerns, potential issues. And caching is more complicated. As I said, not going to go into caching today because we don't have actual actually the time to go into that. But let's start with the security concerns. So in our examples, as you probably might have already noticed that, we are exposing our
Speaker 1: list of users to everyone that has access to our API. That is of course not good. Uh so how can we uh avoid that? Uh suppose that we want only staff users, so those that has that Where user. is staff is true to be able to access those. So the first thing that comes to mind is that. So we go back to our manual resolver generation, and we can See who is the the user that is requesting the data and check if he is a staff or not. raise permission denied of otherwise return the list of users. This also is introducing info
Speaker 1: which is something that when you add Strawberry is going to pass it, which is an object that where you can access the current context of the resolution. And in this case, for example, we are using to access the request object from Django itself. That's the reason why I can retrieve the you the the user that is doing the the the request there. But now it seems that we can't rely on the automatic resolvers anymore, right? Well it's kinda true, but uh strawberry django comes to the rescue again And uh it provides uh provides a lot a set of useful extensions that you can use.
Speaker 1: you can actually uh customize those for your uh own needs but there are some built-in that you can use for example the easy staff one Uh so in this case the user's list we are uh passing easy staff extensions to it, which will Basically uh what will happen there when the user is going to when you the user field is going to be resolved before that Strawberry is going to check the condition which is oh is user dot is staff true if it is um it will allow the resolution to happen otherwise it will fail So now I'm going a bit deeper,
Speaker 1: and the examples here are very similar, but note that there isn't difference. In here we are querying the users in the query type. In here uh we are querying the emails inside the user type.
Speaker 2: So it's called extensions and not error questions
Speaker 1: There are permissions as well in s uh uh but uh like extensions are something that actually acts like a middleware on top of fields And they are required because there are also some some ways to resolve to check for permissions after the object has been resolved. But I I'll I'll I'll also give uh talk a bit and not give an example because we don't have time, but Uh so moving on. Um in here for the no so now uh because we are using the extensions for the staff. for the list of users. Okay, so only staff users can see our users. Great. But maybe we don't want all of those users to see
Speaker 1: the the emails list for the the for our users maybe okay they can list it but only super users can see the emails It's not a problem. We can just go to the user type and use the eSuperuser extension inside the user type. And uh even though the staff users are going to be allowed to list all the all the users, they are not if they try somehow to access the emails, they are going to be presented with an error Just to mention that so you can see that there are a lot of built-in options in there, we have also a Hasperm. Which you can use. User dot Django has some some built-in for that. Can edit, can
Speaker 1: edit, can delete. I don't remember exactly the name. You can define your own. But you can also use custom permissions for that. So if only users that has user dot can see birth date uh to it can see the birth date of a user that can be used to do that check. And as I I was telling now It also provides, I'm not going to show some examples because this starts to get more complicated, but it also provides a way to check for permissioning on top of the object. So if you have a uh when you have uh permissioning on a set obje of objects inside the same model uh so there are third parts that implements
Speaker 1: uh those kind of permissioning checks such as Django Guardian and we actually use Django Guardian as one of the integrations that we have. But uh so you so just so you know that this exists and you can actually do this. And moving on now, we talked about uh security concerns. Let's talk about performance issues. Uh so RefQL has an intrinsic NPLAM issue. Uh can you figure out why? Basically, because the client can choose whatever he wants, like one of the first pro that I have talked about That also means that we can't know beforehand all the combinations of fields that the user is going to request to us.
Speaker 1: So let's look at this example In case uh when listing listing all the users, in case uh the uh the client also requests the list of emails, and suppose that this list is going to return a thousand users We now uh are producing a thousand and one queries in our our database and this is actually the reason why it's called n Clozon uh issue in case someone didn't know that because one query to list the users and one for each user that gets returned thus n plus one One way we can uh fix this issue is by doing uh this at the end.
Speaker 1: Instead of just returning the users, we can use prefetch related, something that Django provides for us. uh to perfect related objects and avoid any plus one issues. But now we have other another issue. Like it's not a major one, it's actually okay Mostly for this example, might not be for other examples, that we are prefetching emails even if the user doesn't ask it. So like if the user if the client just asks for the user's ID, uh why are we still retrieving the emails if we are not going to use it? Uh so it's better than we had before, but still not great. So how can we actually avoid any Pulson issues using the the built-in
Speaker 1: query optimizer extension that we have inside Starware Jingle? So this is showing this is something that I have not showed before, but it's how you define your schema. This is what is going to be exposed in in endpoint using URLs. In Django, uh, but you just add the Django optimizer extension, and that's pretty much it. And what I mean that by that's pretty much it. So the extension is going to uh magically introspect. Your query because your query is using no types that are linked to the models, it uh it can know that oh okay. So I am asking for ID, first name, and then mail inside the
Speaker 1: mails. So, this is the end result that the the optimizer is going to do when querying for those users. It's going to do a dot only for ID, first name, and a prefetch related for doing a custom prefetch in this case because we also only asked for email inside email. So you can see that even it uh the optimization even goes nested in this case Um another example would be the other way around. So suppose that we have uh uh this uh uh an API, an endpoint. Similar to the user. So you can retrieve a user given a PK. In this case, we can retrieve an email given a PK. This is similar to the previous example, but now
Speaker 1: Um this can be resolved with a joint instead of a perfet related, and that's what the optimizer is going to do in this case. He's going to do a select related to do the join on top of the uh should join the user table on the email And also do a and only to only select what you want, and you can see that in this case he's doing a get later to get the email for that ID But not all cases can be automatically optimized for the by the optimizer. This is an example. Suppose that we have a field called age uh which since we have the birth date we can calculate the age very easily and return it as an integer.
Speaker 1: Uh the the code should be correct hopefully but Uh there like when you request age , there is no age more there is no age column in your model So how does the the optimizer know that what you are going to be using inside it? So uh it does not know, but you can give him uh a hint in this case. Uh true uh so when defining your field you can pass only equals true um a list of only And it will know that okay, so in when age gets selected, because I gave him a hint that he should also add to the only
Speaker 1: list birth to date He's going to also add to the only list birth date and you can safely access birth date in here without uh Hubert. ending up having in this case any close on issues again because Django did not load that birth date and now he tries to load it and suddenly it needs to go to the database to fetch it Any combination of only select related, professional related can be used as hints and more recently also annotations, which is still somewhat experimental, but mostly working. So a quick summary to why use the query optimizer. It's straightforward usage, like mostly you need to just enable it and uh and forget about it.
Speaker 1: Uh when You can't just use it, you can use optimization hints, and those works usually very nice. Uh not only it solves any closon issues, but also improves performance Like a quick example, suppose you have a table with, I don't know, fifty fifty columns, and you are only requesting two of those columns. There is no need for you to retrieve the whole the whole table inside your Django model, just return those two. Even though you are still not returning those to the to the client self you are still having a large payload between oh my god I'm so sorry a large payload uh traveling between your database and your API
Speaker 1: which it is like it is a minor improvement but it's still it is there But of course there are some disadvantages and the main one that I would say is that it's not it's not uh possible to solve very complex Issues for especially for an unrelated data, those that you cannot um do a select related, perfect related For those, you can use data loaders, which is a feature that Strawberry itself provides. It is inside its documentation. Usually I use those a lot for some more complex scenarios. It works great. The only downside is that you need to be running ASGI to use data loaders. Some additional features
Speaker 1: provided by Strawberry Django itself. So it's async friendly, you can run it with WSGI or ASGI SYNC or a Sync, whatever you prefer, it works. Running WSGI, the only downside is that you can't use data loaders, but other than that, everything should work great. QG Mutations, it also provides some uh some easily uh to create uh tools to uh to do cute mutations so create update delete uh mutations I have not covered mutations in here But think of then of queries that modify data. Cursor pagination, uh, so there is uh a built-in
Speaker 1: support for uh doing uh cursor pagination you know in an easy way usually limit enough set is enough for most user cases but sometimes you need something more uh advanced it and strawberry does provide a way for you to build that more easily without having to go uh into the bare bones of everything So as you might might have seen in my example, there was a two-debug to bar uh hidden in there, but I uh I can use it to see the queries that when I execute execute a query to see what the big queries actually happened, the performance, and so on. Django channels integration to use
Speaker 1: subscription for real-time data, which is also another functionality of GraphQL itself that I'm not going to cover here. There is a lot to Dig deeper inside GraphQL itself. And well, uh what to do now? Uh I've I have listed here some resources uh which I think those are useful mostly like the strawberry webpage, the documentation, the strawberry documentation, which right now is living in its own website, but it's being merged together with the strawberry main one. Also um be sure to join our strawberry uh our discord channel uh people are
Speaker 1: the community around there is very friendly feel free to ask questions report issues to us And also I've listed some extra GraphQL resources which I think are nice, not actually related to Strawberry itself, but to GraphQL in general. And uh I'm going to show the last page in here, but I think we still have time. I'm not sure though the is the guy here there Because I have some more examples to show, but I'll I I think it would be nice to go with the Q<unk>A first. And that's it. Thank you very much for attending this talk.
Speaker 1: So does anyone have any questions?
Speaker 3: How do you handle these uh reflexive relationship? I mean like a user depending on a user, like with foreign key
Speaker 1: You mean self self-relations?
Speaker 3: Self-relations.
Speaker 1: Yeah, no, those works uh I have I you actually had an example like that, but I decided to remove because it was too complex But the same way as you when you create a data class, you can do another uh you can annotate it to itself. Of course you need to put it in as a string because you it is not ready it should be used. But uh it works literally just like that in GraphQL allows uh types to um Friends is a list of user itself. So it it uh it just works.
Speaker 2: But how do you handle the If the user if if the request we would we require like several nesting levels
Speaker 1: Oh I see, I see.
Speaker 2: I mean this doesn't have an impact on the performance.
Speaker 1: Yeah, um
Speaker 2: and have to have have you a way to avoid that Uh yes.
Speaker 1: Uh so yes it can prov is it can cause some uh some performance impacts Uh there is a there is ways uh to uh overcome this. One way is like there are built-in extensions on strawberry itself where you can oh my god, I need to stop doing that. Uh there is ways for you to be able to limit like the depth that you can dig in into a relation. And like if you try to Go deeper than that, uh strawberry itself is going to raise an error and will not allow you to go there. But uh regarding the Like when you resolve, for example, if I have a user and the user has a name and has friends, so their friends have a name that possibly has friends
Speaker 1: You can just go there indefinitely. You can use fragments as a way to avoid duplicating, which is something for strong like it is Think of it as a template that you can use to query the data inside it so you don't need to repeat everything that you are requesting from the user inside the friends But the the graphical specs themselves does not allow infinite recursion. So you need to define the depth that you are going to go in your client.
Speaker 2: How do the mutations work when you are posting data?
Speaker 1: Okay, so mutations uh uh they are actually really really similar to to queries like in the if you take the the graphical specs uh they define that Queries and mutations are basically the same thing. So as you could see, like for example, queries, I have a user that receives a PK. Well, I can actually pass data and do a create user in there And that's basically what a mutation is going to do. The only difference is how the spec dictates that the server must behave when executing queries and mutations. Uh when you have a query you can do a lot of queries at the same time and uh the spec says that they can be resolved in parallel So if you uh
Speaker 1: if I am asking for, I don't know, three users using the user itself at this in the same query, they can be fetched in parallel, but a mutation should happen sequentially. So if one fails, the other ones should not. uh execute at all and that's basically the the difference but but in when defining in strawberry you are defining uh a field as well and instead of strawberry field you are defining strawberry dot mutation that's the only difference
Speaker 2: And the validation and checks, like the age has to be more than eighteen for a user to be created. So those things
Speaker 1: Yeah, that is uh your uh graphical doesn't dictate uh like uh how You do things. You are returning age, which is an integer. If uh it uh um let's use the mutation example. Oh uh like you are receiving uh um
Speaker 2: a birth date
Speaker 1: and you don't want it to be lower than nine nineteen uh nineteen uh don't know uh You well in your resolver you can add a check for that and raise validation error, for example, just like Django does actually when creating the user Django itself is going to If you have the validation there and the uh and it will fail. Storebury Django does provide a way even for you to return those validation errors as a way to help your front end to show them as just like Django Jimingas. I did not go into details about that in here, but if you want I can show you some examples later.
Speaker 2: And how are the duplication uh messages shown are also are they also shown in the um graft real Answer or just a little bit.
Speaker 1: It depends on how they are going to uh to warn you. Some uh for example there are code generators that you are are going to write a query and for this actually I I made a suggestion about one of the last linking there. That they are going to automatically generate type TypeScript types for you, for your queries. And that one is going to raise warning, just like you when you run ESLynch or Flake H or uh rough uh it's going to say oh this uh this is okay uh you what we are doing but be warned that this is will not be okay anymore soon And uh usually your language server is also going to, when using VS Code or whatever, ID that has a language server that can interpret
Speaker 1: uh graphQL it is probably going to show it and show an error for you oh you are using something that is deprecated so that is a warning for a warning for you uh developer that you should probably not be using that anymore. That's basically how it works.
Speaker 4: I hope you understand the concept of APM version name because you said well if you just write a new attribute like name instead of first name and last name uh it's very problem so but it would affect the price though so how do we uh well i would like to understand the qip first but it's restate because you can If you change the uh attributes of the API resource, it is going to affect the client size.
Speaker 1: So uh so not really, because um you are creating a new uh attribute uh based on that fact no clients still are requesting that attribute so they are not yet receiving name uh so the for for someone that's not in uh uh that is not selecting name yet for then the the response is going to be actually the same. Only when they see that first name was duplicated and they start to to request the name itself it Then that is when like their their uh their payload is going to actually change. So that is basically like you can add Anything that you want extra in your type, as long as the user is not selecting it, it's
Speaker 1: there is no changes for them at all. And as long as you Do not remove anything or like change anything, like change the type. Oh, now name instead of being a string is for some whatever weird reason an integer. That is going to break But uh like you can deprecate name and I don't know create a name as as int and say, oh okay, this is deprecated, you should now use name as inch and parse it to I don't know. Something like that. Did that answer your question? Awesome.
Speaker 3: Yeah, my question is about that magic out of field. Is it possible to make it serialize your own types, your own field types, and also override the default ones?
Speaker 1: Yes, um the so the auto fields by default they have a map That they will link. So for example, Sharfield is mapped to NSCR and so on. And you can the map is public, you can import it and override it. You can even say, okay, so it's Sharpfield is now going to use my uh shark type, which I will have some attributes inside for some reason. Uh you you are uh you are free to uh override those or add more more fields inside it.
Speaker 3: That is the place.
Speaker 1: Yes, that that is that that is the place. If you want a lot more complex stuff maybe uh I don't know if Strawberry GraphQL would would allow it. Strawberry Jengo sorry but like you can always uh uh open an issue contact me and we can see how to change the API to accommodate that But then the simplest answer and I know because I've done a lot I had a I use it to have a lot of custom fields in my projects and instead of allowing strawberry django to use something or it or sometimes it is so custom that it doesn't have like a base to compare, I mapped it to a custom a custom type, for example. one
Speaker 2: question maybe related to what um how do you handle recursion errors because for example user
Speaker 1: let me get callership
Speaker 2: yeah uh my question is maybe more related to what and it's to like how do you handle uh recursion errors because the user model has a list of names and has the user attributes so maybe that's like something You can just pop C recursion for what's important.
Speaker 1: Oh I see, yes. Uh that is uh similar to his question. Um In theory, like uh the graph call itself doesn't dictate what you can or cannot do. In theory, you can just recurse uh in uh infinitely. Um you just can't do that like you can't use fragments for example uh that are recursive but as long as you are willing to actually type everything in there you can But during the execution, Strawberry itself provides extension so you can check, for example, the depth, the depth. stop at at a given point we have we have been also developing some uh some extensions for example there is a a depth
Speaker 1: um A max depth that you can go if you set it for example to far, you can't go uh higher than a lot lower than far. So User emails, user emails, and that's it. You don't you can't get the user anymore. But you can also write your own. It's very easy using the extension provider that we have And also as uh also if there is something that strawberry is not doing and you think it can do better feel free always to open an issue, talk to us, and we can make see check to see a way to make it work there. And I'll also open always open to contributions.
Speaker 1: Um
Speaker 5: uh I haven't used GraphQL so far, but um Does it play well with uh Django raspberry? Because when I see this, uh giving you uh real world scenario, it's like It fits very well the case of our company, where we have a model that has many, many views and over over time it evolved in terms of APIs. really badly. So we have like four or five APIs that fetch from the same model, but returns different fields. And then you never know which API to use or sometimes you need an API that combines two fields that they have no API in common. So when I see this, I see, oh, that's a good way for me to just get all these APIs, throw in the trash , and uh have a uh
Speaker 5: one source of truth to fetch this this disputes and then I can keep for example the creation still on Raspberry Work would they play well together
Speaker 1: Uh yeah, like you can have both exposed because GraphQL is a single point of entry. Like you are going to expose your schema in slash GraphQL. Everything else is there for user. All requests, like requesting user and requesting email, it's going to go to slash GraphQL. So it it um pen so but uh so you can do that but I don't know if the question is if you can adjust your rest um serializers for example to uh to strawberry if and if that's the question uh They are very similar, but we don't have like a Graphene, for example, which is another strawberry library, another Graphic
Speaker 1: Row library. I think they have something similar, but we don't But the way you define is very similar to rest
Speaker 5: framework
Speaker 1: series. Yeah. As you could see, the only difference is that instead of have defining a meta and the fields you define them as auto and when you have like a serializer method you define a custom resolver. So it is The skills translate very well from one to another, but it's not code code-wise like you can just get one and adapt to the other. You might need some more fine adjustments.
Speaker 5: Yeah, but we could still use GraphQL to fetch. And use Resp framework to
Speaker 1: sure. When you get start to fetching, you are going to to tr to want to start mut mutating as well, I'm sure. But yeah, like you can have both uh together. It's not an issue. Uh go go ahead first and then I I have had any
Speaker 2: experience with uh grappling tango.
Speaker 1: Okay.
Speaker 2: What would you say uh is advantage of strobe Are you uh comparable uh graphic?
Speaker 1: Oh so um I think that two main reas two main things that comes to my mind, three actually. The first one is well uh strawberry is uh uh is more ideometric Python. uh it's using type annotations to so you when you see a graphQL type it is very easy for you to see how it will look like on the schema like it's basic it's more almost a one-to-one mapping between both Also, not sure like I use it to use graphene in the past. I I actually before I became a strawberry developer core developer, I I started using it because I was seeking an alternative because Graphene itself um uh it it is going through uh some hard times
Speaker 1: uh like May the main developers from it are not there anymore. There are some some people trying to start maintaining it, but Uh I I don't know how Graphene is going to be in the future. Like it has a very stable API, it just works. But uh in like in just speaking of this point, strawberries very actively maintained, not only me, um like the creator itself, Patrick, a great friend of mine, I was together with him to uh yesterday and he's actively maintaining strawberry we are always uh the community is great if you go to the strawberry uh discord not only me pet itself and anyone
Speaker 1: will always uh welcome you very very warmly even the users in there are going to help you if you need so I would say those are the main uh the main differences Hope that answers. And you have another question.
Speaker 2: Actually, it was more or less the same question asking. I use uh graphene Django and I sometimes have the impression that it was not so well maintained and I wanted to ask if it makes sense to switch to strawberry, but I think
Speaker 1: Yeah, that is uh uh when I started questioning that that is when I started seeking for alternatives. I saw a strawberry, I felt in love with it Then suddenly I was contributing to Strawberry Django. Suddenly Patrick asked me, oh, your contributions are great you wanted to maintain The project, sure, let's let's do this. And when I saw it, I was also a strawberry car developer and maintainer, and here I am today. So when I say like go for strawberry, that is also from the perspective of someone that used it to use graphene and decided to move to strawberry because thought it was better eat in that like two years ago. Or three years ago actually.
Speaker 1: Even more. So a lot more of improvements happened since then. Oh, so yeah, I have just ran off out of time. So thank you all for attending. And if you have any questions, I'll be I'll be around during those three days. So feel free to reach me
GraphQL avoids overfetching and underfetching, lets clients traverse relations, provides type safety and generated documentation, and can evolve fields without API versioning. The main drawbacks discussed are security risks around unrestricted relations, relation-related performance problems, and more complicated caching because requests are usually POSTs.
Discussed at 0:00The client specifies exactly which fields it wants, so the response contains only those fields. The same endpoint can serve a small listing or a more detailed view without creating separate endpoints.
Discussed at 2:17Strawberry wraps Python dataclasses with `@strawberry.type`, using type annotations to generate the GraphQL schema. Resolvers are attached with `strawberry.field`; Strawberry calls the resolver and uses its return type and argument annotations to build the schema.
Discussed at 10:27Use `strawberry_django.type` with the Django model, then mark model-backed fields with `strawberry.auto`. Strawberry Django introspects the model to infer field types and can automatically resolve related fields and root queries, reducing boilerplate.
Discussed at 16:50You can manually inspect the request context and reject users who lack access, or use Strawberry Django extensions such as staff-user and superuser checks. Built-in and custom permission checks can protect whole fields, related fields, or individual objects.
Discussed at 21:39The talk recommends Strawberry Django’s query optimizer extension, which inspects the requested fields and adds suitable `only`, `select_related`, and `prefetch_related` operations, including nested selections. For more complex unrelated data, Strawberry data loaders are another option.
Discussed at 26:34Self-referential types work by referring to the type by name, such as a list of users on a user type. To prevent unbounded nesting and performance problems, Strawberry extensions can impose a maximum query depth; the GraphQL client must also request a finite depth.
Discussed at 37:34Mutations are defined much like queries, but with `strawberry.mutation`, and they perform operations such as creating or updating data. Unlike queries, mutations are executed sequentially according to the GraphQL specification, and validation can be performed in the resolver or through Django validation.
Discussed at 40:15Yes. Strawberry Django’s type mapping is public, so you can override the default mapping or add mappings for custom Django fields and types. For highly specialized behavior, the mapping can point a model field to a custom Strawberry type.
Discussed at 46:07Yes. GraphQL can be exposed as a separate entry point such as `/graphql`, while existing REST endpoints continue handling other operations such as creation. The talk presents GraphQL as a single endpoint for fetching different combinations of fields, rather than requiring all REST APIs to be removed immediately.
Discussed at 49:40Note: We understand that names change, people change, and bodies change. We respect each individual's journey and privacy. If you have any concerns about a video or need us to remove content, please don't hesitate to contact us. We will handle your request with care and promptly address any issues.
Published June 13, 2025
Published June 13, 2025
Published June 13, 2025
Published June 13, 2025
Published June 13, 2025
Published June 13, 2025