GraphQL in Python and Django Patrick Arminio @patrick91 Who am I - - PowerPoint PPT Presentation

graphql in python and django
SMART_READER_LITE
LIVE PREVIEW

GraphQL in Python and Django Patrick Arminio @patrick91 Who am I - - PowerPoint PPT Presentation

GraphQL in Python and Django Patrick Arminio @patrick91 Who am I Patrick Arminio Backend Engineer @ Verve Chairperson at Python Italia @patrick91 online GraphQL? WEB 1.0 WEB 2.0 REST APIs While REST APIs are good, they


slide-1
SLIDE 1

GraphQL in Python and Django

Patrick Arminio @patrick91

slide-2
SLIDE 2

Who am I

  • Patrick Arminio
  • Backend Engineer @ Verve
  • Chairperson at Python Italia
  • @patrick91 online
slide-3
SLIDE 3

GraphQL?

slide-4
SLIDE 4

WEB 1.0

slide-5
SLIDE 5
slide-6
SLIDE 6

WEB 2.0

slide-7
SLIDE 7
slide-8
SLIDE 8

REST APIs

slide-9
SLIDE 9

While REST APIs are good, they have some shortcomings

slide-10
SLIDE 10

Too many API calls (under-fetching)

slide-11
SLIDE 11

http GET /user/1

slide-12
SLIDE 12

{ "name": "Patrick", "friends": [ "/users/2", "/users/3", "/users/4" ], "avatar": "/images/123" }

slide-13
SLIDE 13

http GET /user/2 http GET /user/3 http GET /user/4

slide-14
SLIDE 14

http GET /user_with_friends/1

slide-15
SLIDE 15

{ "name": "Patrick", "friends": [ { "name": "Fiorella" }, { "name": "Marco" }, { "name": "Marta" } ], "avatar": "/images/123" }

slide-16
SLIDE 16

http GET /user_with_friends/1

slide-17
SLIDE 17

http GET /user_with_friends/1 http GET /user_with_friends_and_avatar/1

slide-18
SLIDE 18

http GET /user_with_friends/1 http GET /user_with_friends_and_avatar/1 http GET /user_with_avatar/1

slide-19
SLIDE 19

http GET /user_with_friends/1 http GET /user_with_friends_and_avatar/1 http GET /user_with_avatar/1 http GET /user_with_small_avatar/1

slide-20
SLIDE 20

http GET /user_with_friends/1 http GET /user_with_friends_and_avatar/1 http GET /user_with_avatar/1 http GET /user_with_small_avatar/1 http GET /user_with_small_avatar_and_friends/1

slide-21
SLIDE 21

http GET /user_with_friends/1 http GET /user_with_friends_and_avatar/1 http GET /user_with_avatar/1 http GET /user_with_small_avatar/1 http GET /user_with_small_avatar_and_friends/1 http GET /page-1

slide-22
SLIDE 22

http GET /user_with_friends/1 http GET /user_with_friends_and_avatar/1 http GET /user_with_avatar/1 http GET /user_with_small_avatar/1 http GET /user_with_small_avatar_and_friends/1 http GET /page-1 http GET /page-2

slide-23
SLIDE 23

[...] At the time, we had

  • ver 1,000 different REST

endpoints at Coursera (and now we have many more) [...]

Source: Coursera https://dev-blog.apollodata.com/courseras-journey-to-graphql-a5ad3b77f39a

slide-24
SLIDE 24

Too much data (over-fetching)

slide-25
SLIDE 25

{ "name": "Patrick", "friends": [{ "name": "Ernesto", "friends": ["/users/2", "/users/3", "/users/4"], "avatar": { "url": "//cdn.x.com/123.jpg", "width": 400, "height": 300 } }, { "name": "Simone", "friends": ["/users/2", "/users/3", "/users/4"], "avatar": { "url": "//cdn.x.com/123.jpg", "width": 400, "height": 300 } }, { "name": "Marta", "friends": ["/users/2", "/users/3", "/users/4"], "avatar": { "url": "//cdn.x.com/123.jpg", "width": 400, "height": 300 } } ], "avatar": { "url": "//cdn.x.com/123.jpg", "width": 400, "height": 300 } }

slide-26
SLIDE 26
slide-27
SLIDE 27

REST AND HYPERMEDIA LINKS ARE GREAT, BUT NOT ALWAYS THE BEST CHOICE WHEN BUILDING WEBSITES OR APPS.

slide-28
SLIDE 28

Documentation

slide-29
SLIDE 29
slide-30
SLIDE 30

Can we do better?

slide-31
SLIDE 31

We could extend REST, but...

slide-32
SLIDE 32

There won’t be a standard way

slide-33
SLIDE 33

GraphQL! ✨

slide-34
SLIDE 34

GraphQL is a Query Language for APIs.

Source: https://graphql.org/

slide-35
SLIDE 35

GraphQL is a specification

slide-36
SLIDE 36

Single HTTP endpoint

slide-37
SLIDE 37

http POST /graphql

slide-38
SLIDE 38

{ user(id: "1") { name friends { name } avatar } }

slide-39
SLIDE 39

{ "user": { "name": "Patrick", "friends": [ { "name": "Fiorella" }, { "name": "Marco" }, { "name": "Marta" } ], "avatar": "/images/123" } }

slide-40
SLIDE 40

GraphQL is typed

slide-41
SLIDE 41

type Query { user(id: ID!): User } type User { name: String! friends: [Friend!]! avatar: String! } type Friend { name: String! }

slide-42
SLIDE 42

type Query { user(id: ID!): User } type User { name: String! friends: [Friend!]! avatar: String! } type Friend { name: String! }

slide-43
SLIDE 43

type Query { user(id: ID!): User } type User { name: String! friends: [Friend!]! avatar: String! } type Friend { name: String! }

slide-44
SLIDE 44

type Query { user(id: ID!): User } type User { name: String! friends: [Friend!]! avatar: String! } type Friend { name: String! }

slide-45
SLIDE 45

type Query { user(id: ID!): User } type User { name: String! friends: [Friend!]! avatar: String! } type Friend { name: String! }

slide-46
SLIDE 46

type Query { user(id: ID!): User } type User { name: String! friends: [Friend!]! avatar: String! } type Friend { name: String! }

slide-47
SLIDE 47

Scalar Types

  • Int
  • Float
  • String
  • Boolean
  • Any user defined scalars (IE. datetime)
slide-48
SLIDE 48

Object Types are the objects defined in your GraphQL

  • API. They are objects that have fields that can of scalar

types or other object types.

Object Types

slide-49
SLIDE 49

Type “modifiers”

  • List
  • Non-nulls
slide-50
SLIDE 50

Why is this important?

slide-51
SLIDE 51

Static checking

slide-52
SLIDE 52

Documentation and introspection

slide-53
SLIDE 53

Let’s see an example

slide-54
SLIDE 54
slide-55
SLIDE 55

Operations

slide-56
SLIDE 56

3 Main operations

Subscription Allows to subscribe to events, for example when a new user has been created. Mutation Allows you to modify/create data on the server. But it is not limited to data, can be used to run anything with side effects. Query Allows to request data from the server.

slide-57
SLIDE 57

Query (shortcut)

{ user(id: "1") { name } }

slide-58
SLIDE 58

Query

query QueryName($id: ID!) { user(id: $id) { name } }

slide-59
SLIDE 59

Mutation

mutation MutationName($input: CreateUserInput!) { createUser(input: $input) {

  • k

} }

slide-60
SLIDE 60

Subscription

subscription SubscriptionName {

  • nUserCreated {

name } }

slide-61
SLIDE 61

Intermission

slide-62
SLIDE 62

GraphQL in Python

slide-63
SLIDE 63

2 libraries

slide-64
SLIDE 64

Ariadne

https://github.com/mirumee/ariadne/

  • Quite new
  • Python 3.5+
  • “Closer to GraphQL”
slide-65
SLIDE 65

Graphene

https://graphene-python.org/

  • Most popular
  • Python 2.7+ and Python 3.5+
  • Nice abstraction on top of GraphQL
  • Support for Django and more frameworks
slide-66
SLIDE 66

Let’s start with Ariadne

slide-67
SLIDE 67

We need a schema

type Query { user(id: ID!): User } type User { name: String! friends: [Friend!]! avatar: String! } type Friend { name: String! }

slide-68
SLIDE 68

We need a schema

type Query { user(id: ID!): User } type User { name: String! friends: [Friend!]! avatar: String! } type Friend { name: String! }

slide-69
SLIDE 69

We need a schema

type Query { user(id: ID!): User } type User { name: String! friends: [Friend!]! avatar: String! } type Friend { name: String! }

slide-70
SLIDE 70

How do we link data to the fields?

slide-71
SLIDE 71

Resolvers

slide-72
SLIDE 72

Each field on each type is backed by a function called the resolver which is provided by the GraphQL server developer.

slide-73
SLIDE 73

A simple resolver

def resolve_user(_, info, id): return { "name": "Patrick", "friends": [ {"name": "Fiorella"}, {"name": "Marco"}, {"name": "Marta"}, ], "avatar": "/images/123", }

slide-74
SLIDE 74

Attaching a resolver to a Type

resolvers = { "Query": {"user": resolve_user}, "User": {"name": resolve_name}, }

slide-75
SLIDE 75

Creating and running the server

server = GraphQLMiddleware.make_simple_server( schema, resolvers ) server.serve_forever()

slide-76
SLIDE 76

Done!

slide-77
SLIDE 77
slide-78
SLIDE 78

Intermission

slide-79
SLIDE 79

Graphene

slide-80
SLIDE 80

The schema is defined in Python

slide-81
SLIDE 81

Our schema

type Friend { name: String! } type User { name: String! friends: [Friend!]! avatar: String! } type Query { user(id: ID!): User }

slide-82
SLIDE 82

Defining types with Graphene - Friend

class FriendType(graphene.ObjectType): name = graphene.String(required=True)

slide-83
SLIDE 83

Types and resolvers live together*

* resolvers can also be external functions

slide-84
SLIDE 84

Defining types with Graphene - User

class UserType(graphene.ObjectType): name = graphene.String(required=True) friends = graphene.List(graphene.NonNull(FriendType)) avatar = graphene.String(required=True) def resolve_friends(self, info): return [ FriendType(name="Marta"), FriendType(name="Marco"), FriendType(name="Fiorella"), ]

slide-85
SLIDE 85

Defining types with Graphene - Query

class Query(graphene.ObjectType): user = graphene.Field( UserType, id=graphene.ID() ) def resolve_user(self, info, id): return UserType( name="Patrick", avatar="/images/123" )

slide-86
SLIDE 86

Finally, the schema

schema = graphene.Schema(query=Query)

slide-87
SLIDE 87

Done!

slide-88
SLIDE 88

Django Support

Graphene has support for Django, meaning that:

  • Has a built in view
  • It can create types from django models
  • It can create mutations from Forms and DRF Serializers
  • Has support for django filters
slide-89
SLIDE 89

What about

slide-90
SLIDE 90

Authentication

slide-91
SLIDE 91

Authentication

When using GraphQL with HTTPs you have 3 options for authentication:

  • Sessions
  • HTTP Headers
  • Field arguments
slide-92
SLIDE 92

Sessions

Basically you rely on the browser sending cookies to your backend service, this works pretty well with Django. Good when you an API that works only with your frontend and when you don’t have a mobile application.

slide-93
SLIDE 93

Headers

You can use headers when you have third party clients accessing your API or when you have a mobile app. Usually it is used in combination with JWT tokens.

slide-94
SLIDE 94

Field params

This might be a good solution when you only have a few fields that require authentication. It could work like this:

{ myBankStatement(token: "ABC123") { date amount } }

slide-95
SLIDE 95

Security

slide-96
SLIDE 96

Quite easy to create “malicious” queries

slide-97
SLIDE 97

{ thread(id: "some-id") { messages(first: 99999) { thread { messages(first: 99999) { thread { messages(first: 99999) { thread { # ...repeat times 10000... } } } } } } }

slide-98
SLIDE 98

Solution for “malicious” queries

To prevent bad queries to happen we can adopt various solutions:

  • Timeouts
  • Limits on nested fields
  • Query cost
  • Static queries
slide-99
SLIDE 99

Timeouts

Check how long a query is taking, if it is taking more than 1 second you can kill it.

  • Prevents huge queries from DOS-ing your server
  • Prevents long waiting time
slide-100
SLIDE 100

Limit on nested fields

You can parse the incoming GraphQL request and deny queries that are requesting for fields that are too nested. For example you can

  • nly allow for maxing 3 levels of nesting and no more.

Easy solution when you don’t need complex checks.

slide-101
SLIDE 101

Query costs

This is useful if you have third party clients and when you also want to limit their API usage. The idea is to give each field a cost and calculate the cost of the query based on the number of fields requested. This works extremely well with paginated data (where you know how much data you’re asking for)

slide-102
SLIDE 102

Query costs - example query

query { viewer { repositories(first: 50) { issues(first: 10) { title } } } }

slide-103
SLIDE 103

Query costs - calculating the cost

50 = 50 repositories + 50 x 10 = 500 repository issues = 550 total nodes

slide-104
SLIDE 104

Static queries

Instead of allowing any query to be ran on your API you could allow

  • nly a predefined list of queries. You’d save those queries on a

database and reference them by ID. So instead of doing a request passing the query to GraphQL you’d pass only the ID (and the variables if any).

slide-105
SLIDE 105

http POST /graphql?id=123

slide-106
SLIDE 106

Static queries

  • Good to prevent unwanted queries
  • Still allows to use all the advantages of GraphQL
  • A bit cumbersome to deploy
  • If you have third party you need a way for them to declare

queries

  • Potentially good for caching (see next slide)
slide-107
SLIDE 107

http GET /graphql?id=123

slide-108
SLIDE 108

Caching

slide-109
SLIDE 109

Client Caching

slide-110
SLIDE 110

Network Caching

slide-111
SLIDE 111

Application Caching

slide-112
SLIDE 112

Additional Things

slide-113
SLIDE 113

Arguments and Inputs

slide-114
SLIDE 114

{ search(text: "an") { title } }

slide-115
SLIDE 115

{ createUser(input: { … }) { user { name } } }

slide-116
SLIDE 116

Input Types

slide-117
SLIDE 117

input CreateUserInput { name: String! age: Int }

slide-118
SLIDE 118

Enums

slide-119
SLIDE 119

enum Conference { PYPARIS PYCONX PYCONUS }

slide-120
SLIDE 120

Interfaces

slide-121
SLIDE 121

interface Character { id: ID! name: String! } type Human implements Character { id: ID! name: String! friends: [Character] starships: [Starship] }

slide-122
SLIDE 122

union SearchResult = Human | Droid { search(text: "an") { ... on Human { name height } ... on Droid { name primaryFunction } } }

slide-123
SLIDE 123

Errors

slide-124
SLIDE 124
slide-125
SLIDE 125

And more

slide-126
SLIDE 126

Frontend

slide-127
SLIDE 127

Frontend developers benefit a lot from GraphQL, thanks to all the tooling available.

slide-128
SLIDE 128

Relay

https://facebook.github.io/relay/

  • Made by Facebook
  • React Only
slide-129
SLIDE 129

Apollo

https://www.apollographql.com/

  • Supports many frameworks (React, Vue, etc)
  • Big community
  • Lots of tooling
slide-130
SLIDE 130

Verve is Hiring 🎊

Want to work in an amazing company and use Python 3, GraphQL and Django? https://verve.co/careers/

slide-131
SLIDE 131

THANKS!

Patrick Arminio @patrick91