Quick Start¶
Installation¶
JWT Authentication¶
The JWT part of this package is heavily based on the django-graphql-jwt package. Noting that, not all features supported by that package are supported here. For details on what is supported please refer to the the JWT part of these docs.
Add the JWT Authentication Backend to your settings¶
| settings.py | |
|---|---|
Add the JWT Middleware to your settings¶
Note
This implementation of the middleware is different from other implementations in that it is designed to handle token based authentication for all request containing the Authorization header regardless of wether the request is to be consumed by your GraphQL view. This aims to provide a unified way of authenticating via JWT tokens issued by this package accross your entire application.
If the request contains the Authorization header and the token is valid, the user will be authenticated and the request.user will be set to the user associated with the token. If the token is expired a 401 response will be returned along with Token Expired. If an invalid token is provided a 401 response will be returned along with Invalid Token.
If the user is already authenticated by some previous middleware in your middleware stack it will be respected and the token will not be checked at all. The order of the middleware in your middleware stack is important and you can set it depending on your needs.
Expose the mutations in your GraphQL schema¶
| schema.py | |
|---|---|
Override any settings you might need in your project settings.¶
If you set JWT_LONG_RUNNING_REFRESH_TOKEN to True you will need to add the following to your settings file:
| settings.py | |
|---|---|
and run python manage.py migrate to create the refresh token model.
If you set JWT_AUTHENTICATE_INTROSPECTION to True you will need to add an extension to the root of your schema:
| schema.py | |
|---|---|
Mutation Hooks¶
Mutation hooks are provided via a field_extension and can be applied to any strawberry mutation.
| hooks.py | |
|---|---|
and then applied to your mutation:
Note
You might have noticed that we are passing both a sync and an async function at the same time. This is possible because if the context is async the sync function will be wrapped with sync_to_async and awaited. If the context is sync passing post_async and pre_async will be ignored. In either case the async functions are awaited.
Input Validations¶
Much inspired by the way graphene-django-cud handles validation, this package provides a similar way to validate your input when the respective input classes are instantiated.
When updating an existing object the pk will be available through self.id so you can validate values in comparison to existing ones. Also info is provided in case validations need to be run against the user making the request.
Finally add this to each mutation that needs to run validations:
| schema.py | |
|---|---|
Permissions¶
Similarly to validations, permission checking is run on input instantiation. Since strawberry does not currently provide a way to pass permission_classes to input fields this package allows you to write your permission checking functions as part of the input class.
| schema.py | |
|---|---|
Note
As documented by Strawberry extension order does matter so make sure you are passing the with_permissions() and with_validation() extensions in an order that makes sense for your project.
Nested Mutations¶
This package provides support for deeply nested mutations through a field extension and some wrapper input classes.
It makes sense for the inputs to be different when updating an object vs creating one. So we provide different input wrappers for each type of operation. It also makes sense that the api provided would be different depending on the type of relationship between the related models. Brief explanations of each input wrapper is provided below. For details refer to relevant guide on nested mutations.
Wrappers for nested objects for create mutations¶
One to One¶
CRUDOneToOneCreateInput can be used when you want to create or assign a related object, alongside the creation of your root object. The resulting schema will provide two actions for your mutation create and assign which are mutually exclusive. create is of type UserCreateInput which you will need to provide and assign is of type ID. A brief example follows.
Now we can create or assign nested objects on either side of the relationship.
One to Many¶
CRUDOneToManyCreateInput. Similarly to the one to one wrapper it provides two actions create and assign which are mutually exclusive. The only difference is that the relationship is through a ForeignKey meaning the other side of the relationship would be Many to One requiring a different wrapper.
Many to One¶
CRUDManyToOneCreateInput. This wrapper is used when the relationship is Many to One. It provides two actions create and assign which are NOT mutually exclusive. The inputs are of course lists and of type SomeModelInput and ID respectively.
Many to Many¶
CRUDManyToManyCreateInput is provided for Many-to-Many relationships. It provides two actions create and assign which are NOT mutually exclusive. The inputs are ofcourse lists again but there's one important difference. They are internally wrapped again to provide a mechanism for the user to provide through_defaults for the relationship either on assignment or creation. The type for through_defaults is JSON and the values should follow snake case.
Wrappers for nested objects for update mutations¶
These wrappers expect two inputs to be provided instead of the one that was necessary for creation. The first is for creation of new related objects when updating the current object and the second is for updates to the data of already related objects.
One to One¶
CRUDOneToOneUpdateInput can be used when alongside an update mutation you want to update related objects. The resulting schema will provide three possible actions for your mutation and a boolean flag.
createof typeUserInputused to create a new related object.assignof typeIDused to assign an existing objects as related.Note that you can use
nullto remove the relationship if the field is nullable.updateof typeUserPartialused to update the fields of an existing related object.deleteof typeboolindicating whether the related object should be deleted.Note that this flag can be used together with assign or create to delete the previously related object.
One to Many¶
CRUDOneToManyUpdateInput can be used when alongside an update mutation you want to update related objects. The resulting schema will provide three possible actions for your mutation and a boolean flag. These are the same as the ones provided by the One to One wrapper, and they function in exactly the same fashion.
For a more detailed explanation with examples please refer to the relevant guide on nested mutations.
Many to One¶
CRUDManyToOneUpdateInput can be used when alongside an update mutation you want to update related objects. The resulting schema will provide four possible actions for your mutation. These are as follows:
createof typeList[UserInput]used to create new related objects.assignof typeList[ID]used to assign relations with existing objects.updateof typeList[UserPartial]used to update the fields of existing related objects.removeof typeList[CRUDRemoveInput]which wraps anIDand aboolflag indicating whether the removed object should be deleted.
Many to Many¶
CRUDManyToManyUpdateInput can be used when alongside an update mutation you want to update related objects. The resulting schema will provide four possible actions for your mutation. These are as follows:
createof typeList[CRUDManyToManyItem]which wraps two inputs.objectDataof typeUserInputused for the fields of the related object.throughDefaultsof typeJSONused for any data stored in thethroughmodel if one exists.
assignof typeList[CRUDManyToManyID]which wraps two inputs.idof typeIDused to assign relations with existing objects.throughDefaultsof typeJSONused for any data stored in thethroughmodel if one exists.
updateof typeList[CRUDManyToManyItemUpdate]which wraps two inputs.objectDataof typeUserPartialused to update the fields of the related object.throughDefaultsof typeJSONused to update the fields of thethroughmodel if one exists.
removeof typeList[CRUDRemoveInput]which wraps anIDand aboolflag indicating whether the removed object should be deleted.
For a more detailed explanation with examples please refer to the relevant guide on nested mutations.