Construyendo una API GraphQL con Django

Las API web son los motores que impulsan la mayoría de nuestras aplicaciones en la actualidad. Durante muchos años, REST ha sido la arquitectura dominante para las API, pero en este artículo vamos a...

Introducción

Las API web son los motores que impulsan la mayoría de nuestras aplicaciones en la actualidad. Durante muchos años, REST ha sido la arquitectura dominante para las API, pero en este artículo exploraremos [GráficoQL] (https://graphql.org/learn/).

Con las API REST, generalmente crea direcciones URL para cada objeto de datos a los que se puede acceder. Digamos que estamos construyendo una API REST para películas: tendremos URL para las películas en sí, actores, premios, directores, productores… ¡ya se está volviendo difícil de manejar! Esto podría significar muchas solicitudes para un lote de datos relacionados. Imagina que eres el usuario de un teléfono móvil de baja potencia con una conexión a Internet lenta, esta situación no es la ideal.

GraphQL no es una arquitectura de API como REST, es un lenguaje que nos permite compartir datos relacionados de una manera mucho más fácil. Lo usaremos para diseñar una API para películas. Luego, veremos cómo la biblioteca Graphene nos permite crear API en Python creando una API de película con [Django] (https://www.djangoproject.com/).

Qué es GraphQL

Originalmente creado por Facebook, pero ahora desarrollado bajo la Fundación GraphQL, GraphQL es un lenguaje de consulta y tiempo de ejecución del servidor que nos permite recuperar y manipular datos.

Aprovechamos el sistema fuertemente tipado de GraphQL para definir los datos que queremos que estén disponibles para la API. Luego creamos un esquema para la API: el conjunto de consultas permitidas para recuperar y modificar datos.

Diseño de un esquema de película

Creando nuestros tipos

Los tipos describen el tipo de datos que están disponibles en la API. Ya se proporcionan tipos primitivos que podemos usar, pero también podemos definir nuestros propios tipos personalizados.

Considere los siguientes tipos para actores y películas:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
type Actor {
  id: ID!
  name: String!
}

type Movie {
  id: ID!
  title: String!
  actors: [Actor]
  year: Int!
}

El tipo ID nos dice que el campo es el identificador único para ese tipo de datos. Si el ID no es una cadena, el tipo necesita una forma de serializarse en una cadena para que funcione.

Nota: El signo de exclamación significa que el campo es obligatorio.

También notará que en Movie usamos tipos primitivos como String e Int, así como nuestro tipo Actor personalizado.

Si queremos que un campo contenga la lista del tipo, lo encerramos entre corchetes - [Actor].

Creación de consultas

Una consulta especifica qué datos se pueden recuperar y qué se requiere para llegar a ellos:

1
2
3
4
5
6
type Query {
  actor(id: ID!): Actor
  movie(id: ID!): Movie
  actors: [Actor]
  movies: [Movie]
}

Este tipo de ‘Consulta’ nos permite obtener los datos de ‘Actor’ y ‘Película’ proporcionando sus ‘ID’, o podemos obtener una lista de ellos sin filtrar.

Creación de mutaciones {#creación de mutaciones}

Una mutación describe qué operaciones se pueden realizar para cambiar los datos en el servidor.

Las mutaciones dependen de dos cosas:

  • Entradas: tipos especiales que solo se usan como argumentos en una mutación cuando queremos pasar un objeto completo en lugar de campos individuales.
  • Cargas útiles: tipos regulares, pero por convención los usamos como salidas para una mutación para que podamos ampliarlos fácilmente a medida que evoluciona la API.

Lo primero que hacemos es crear los tipos de entrada:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
input ActorInput {
  id: ID
  name: String!
}

input MovieInput {
  id: ID
  title: String
  actors: [ActorInput]
  year: Int
}

Y luego creamos los tipos de carga útil:

1
2
3
4
5
6
7
8
9
type ActorPayload {
  ok: Boolean
  actor: Actor
}

type MoviePayload {
  ok: Boolean
  movie: Movie
}

Tome nota del campo ok, es común que los tipos de carga útil incluyan metadatos como un estado o un campo de error.

El tipo Mutación lo reúne todo:

1
2
3
4
5
6
type Mutation {
  createActor(input: ActorInput) : ActorPayload
  createMovie(input: MovieInput) : MoviePayload
  updateActor(id: ID!, input: ActorInput) : ActorPayload
  updateMovie(id: ID!, input: MovieInput) : MoviePayload
}

El mutador createActor necesita un objeto ActorInput, que requiere el nombre del actor.

El mutador updateActor requiere el ID del actor que se actualiza, así como la información actualizada.

Lo mismo sigue para los mutantes de la película.

Nota: Si bien ActorPayload y MoviePayload no son necesarios para una mutación exitosa, es una buena práctica que las API brinden comentarios cuando procesan una acción.

Definición del esquema

Finalmente, asignamos las consultas y mutaciones que hemos creado al esquema:

1
2
3
4
schema {
  query: Query
  mutation: Mutation
}

Uso de la biblioteca de grafeno

GraphQL es independiente de la plataforma, uno puede crear un servidor GraphQL con una variedad de lenguajes de programación (Java, PHP, Go), marcos (Node.js, Symfony, Rails) o plataformas como Apollo.

Con Grafeno, no tenemos que usar la sintaxis de GraphQL para crear un esquema, ¡solo usamos Python! Esta biblioteca de código abierto también se ha integrado con Django para que podamos crear esquemas haciendo referencia a los modelos de nuestra aplicación.

Configuración de la aplicación {#configuración de la aplicación}

Entornos virtuales

Se considera una buena práctica crear entornos virtuales para proyectos de Django. Desde Python 3.6, se ha incluido el módulo venv para crear y gestionar entornos virtuales.

Usando la terminal, ingrese a su espacio de trabajo y cree la siguiente carpeta:

1
2
$ mkdir django_graphql_movies
$ cd django_graphql_movies/

Ahora crea el entorno virtual:

1
$ python3 -m venv env

Debería ver una nueva carpeta env en su directorio. Necesitamos activar nuestro entorno virtual, para que cuando instalemos paquetes de Python solo estén disponibles para este proyecto y no para todo el sistema:

1
$ . env/bin/activate

Nota: Para salir del entorno virtual y usar su shell habitual, escriba deactivate. Debes hacer esto al final del tutorial.

Instalación y configuración de Django y Graphene {#instalación y configuración de Django y Graphene}

Mientras estamos en nuestro entorno virtual, usamos pip para instalar Django y la biblioteca Graphene:

1
2
$ pip install Django
$ pip install graphene_django

Luego creamos nuestro proyecto Django:

1
$ django-admin.py startproject django_graphql_movies .

Un proyecto de Django puede constar de muchas aplicaciones. Las aplicaciones son componentes reutilizables dentro de un proyecto y es una buena práctica crear nuestro proyecto con ellas. Vamos a crear una aplicación para nuestras películas:

1
2
$ cd django_graphql_movies/
$ django-admin.py startapp movies

Antes de trabajar en nuestra aplicación o ejecutarla, sincronizaremos nuestras bases de datos:

1
2
3
4
# First return to the project's directory
$ cd ..
# And then run the migrate command
$ python manage.py migrate

Crear un modelo {#crear un modelo}

Los modelos de Django describen el diseño de la base de datos de nuestro proyecto. Cada modelo es una clase de Python que generalmente se asigna a una tabla de base de datos. Las propiedades de clase se asignan a las columnas de la base de datos.

Escribe el siguiente código en django_graphql_movies/movies/models.py:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
from django.db import models

class Actor(models.Model):
    name = models.CharField(max_length=100)

    def __str__(self):
        return self.name

    class Meta:
        ordering = ('name',)

class Movie(models.Model):
    title = models.CharField(max_length=100)
    actors = models.ManyToManyField(Actor)
    year = models.IntegerField()

    def __str__(self):
        return self.title

    class Meta:
        ordering = ('title',)

Al igual que con el esquema GraphQL, el modelo ‘Actor’ tiene un nombre, mientras que el modelo ‘Película’ tiene un título, una relación de muchos a muchos con los actores y un año. Django genera automáticamente los ID para nosotros.

Ahora podemos registrar nuestra aplicación de películas dentro del proyecto. Vaya a django_graphql_movies/settings.py y cambie INSTALLED_APPS a lo siguiente:

1
2
3
4
5
6
7
8
9
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django_graphql_movies.movies',
]

Asegúrese de migrar su base de datos para mantenerla sincronizada con nuestros cambios de código:

1
2
$ python manage.py makemigrations
$ python manage.py migrate

Cargando datos de prueba

Después de construir nuestra API, querremos poder realizar consultas para probar si funciona. Carguemos algunos datos en nuestra base de datos, guarde el siguiente JSON como movies.json en el directorio raíz de su proyecto:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
[
  {
    "model": "movies.actor",
    "pk": 1,
    "fields": {
      "name": "Michael B. Jordan"
    }
  },
  {
    "model": "movies.actor",
    "pk": 2,
    "fields": {
      "name": "Sylvester Stallone"
    }
  },
  {
    "model": "movies.movie",
    "pk": 1,
    "fields": {
      "title": "Creed",
      "actors": [1, 2],
      "year": "2015"
    }
  }
]

Y ejecute el siguiente comando para cargar los datos de prueba:

1
$ python manage.py loaddata movies.json

Debería ver el siguiente resultado en la terminal:

1
Installed 3 object(s) from 1 fixture(s)

Creando nuestro esquema con grafeno

Realización de consultas {#realización de consultas}

En nuestra carpeta de aplicaciones de películas, cree un nuevo archivo schema.py y definamos nuestros tipos de GraphQL:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import graphene
from graphene_django.types import DjangoObjectType, ObjectType
from django_graphql_movies.movies.models import Actor, Movie

# Create a GraphQL type for the actor model
class ActorType(DjangoObjectType):
    class Meta:
        model = Actor

# Create a GraphQL type for the movie model
class MovieType(DjangoObjectType):
    class Meta:
        model = Movie

Con la ayuda de Graphene, para crear un tipo GraphQL simplemente especificamos qué modelo de Django tiene las propiedades que queremos en la API.

En el mismo archivo, agregue el siguiente código para crear el tipo Query:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# Create a Query type
class Query(ObjectType):
    actor = graphene.Field(ActorType, id=graphene.Int())
    movie = graphene.Field(MovieType, id=graphene.Int())
    actors = graphene.List(ActorType)
    movies= graphene.List(MovieType)

    def resolve_actor(self, info, **kwargs):
        id = kwargs.get('id')

        if id is not None:
            return Actor.objects.get(pk=id)

        return None

    def resolve_movie(self, info, **kwargs):
        id = kwargs.get('id')

        if id is not None:
            return Movie.objects.get(pk=id)

        return None

    def resolve_actors(self, info, **kwargs):
        return Actor.objects.all()

    def resolve_movies(self, info, **kwargs):
        return Movie.objects.all()

Cada propiedad de la clase Query corresponde a una consulta de GraphQL:

  • Las propiedades actor y movie devuelven un valor de ActorType y MovieType respectivamente, y ambas requieren un ID que sea un número entero.

  • Las propiedades actors y movies devuelven una lista de sus respectivos tipos.

Los cuatro métodos que creamos en la clase Query se llaman resolvers. Los solucionadores conectan las consultas en el esquema con las acciones reales realizadas por la base de datos. Como es estándar en Django, interactuamos con nuestra base de datos a través de modelos.

Considere la función resolve_actor. Recuperamos la ID de los parámetros de consulta y devolvemos el actor de nuestra base de datos con esa ID como su clave principal. La función resolve_actors simplemente obtiene todos los actores en la base de datos y los devuelve como una lista.

Hacer mutaciones

Cuando diseñamos el esquema, primero creamos tipos de entrada especiales para nuestras mutaciones. Hagamos lo mismo con Graphene, agregue esto a schema.py:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# Create Input Object Types
class ActorInput(graphene.InputObjectType):
    id = graphene.ID()
    name = graphene.String()

class MovieInput(graphene.InputObjectType):
    id = graphene.ID()
    title = graphene.String()
    actors = graphene.List(ActorInput)
    year = graphene.Int()

Son clases simples que definen qué campos se pueden usar para cambiar datos en la API.

Crear mutaciones requiere un poco más de trabajo que crear consultas. Agreguemos las mutaciones para los actores:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# Create mutations for actors
class CreateActor(graphene.Mutation):
    class Arguments:
        input = ActorInput(required=True)

    ok = graphene.Boolean()
    actor = graphene.Field(ActorType)

    @staticmethod
    def mutate(root, info, input=None):
        ok = True
        actor_instance = Actor(name=input.name)
        actor_instance.save()
        return CreateActor(ok=ok, actor=actor_instance)

class UpdateActor(graphene.Mutation):
    class Arguments:
        id = graphene.Int(required=True)
        input = ActorInput(required=True)

    ok = graphene.Boolean()
    actor = graphene.Field(ActorType)

    @staticmethod
    def mutate(root, info, id, input=None):
        ok = False
        actor_instance = Actor.objects.get(pk=id)
        if actor_instance:
            ok = True
            actor_instance.name = input.name
            actor_instance.save()
            return UpdateActor(ok=ok, actor=actor_instance)
        return UpdateActor(ok=ok, actor=None)

Recuerde la firma de la mutación createActor cuando diseñamos nuestro esquema:

1
createActor(input: ActorInput) : ActorPayload
  • El nombre de nuestra clase corresponde al nombre de consulta de GraphQL.
  • Las propiedades de la clase ‘Argumentos’ internas corresponden a los argumentos de entrada para el mutador.
  • Las propiedades ok y actor conforman ActorPayload.

Lo más importante que debe saber al escribir un método de mutación es que está guardando los datos en el modelo de Django:

  • Tomamos el nombre del objeto de entrada y creamos un nuevo objeto Actor.
  • Llamamos a la función save para que nuestra base de datos se actualice y devuelva el payload al usuario.

La clase UpdateActor tiene una configuración similar con lógica adicional para recuperar el actor que se está actualizando y cambiar sus propiedades antes de guardar.

Ahora agreguemos la mutación para películas:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# Create mutations for movies
class CreateMovie(graphene.Mutation):
    class Arguments:
        input = MovieInput(required=True)

    ok = graphene.Boolean()
    movie = graphene.Field(MovieType)

    @staticmethod
    def mutate(root, info, input=None):
        ok = True
        actors = []
        for actor_input in input.actors:
          actor = Actor.objects.get(pk=actor_input.id)
          if actor is None:
            return CreateMovie(ok=False, movie=None)
          actors.append(actor)
        movie_instance = Movie(
          title=input.title,
          year=input.year
          )
        movie_instance.save()
        movie_instance.actors.set(actors)
        return CreateMovie(ok=ok, movie=movie_instance)


class UpdateMovie(graphene.Mutation):
    class Arguments:
        id = graphene.Int(required=True)
        input = MovieInput(required=True)

    ok = graphene.Boolean()
    movie = graphene.Field(MovieType)

    @staticmethod
    def mutate(root, info, id, input=None):
        ok = False
        movie_instance = Movie.objects.get(pk=id)
        if movie_instance:
            ok = True
            actors = []
            for actor_input in input.actors:
              actor = Actor.objects.get(pk=actor_input.id)
              if actor is None:
                return UpdateMovie(ok=False, movie=None)
              actors.append(actor)
            movie_instance.title=input.title
            movie_instance.year=input.year
            movie_instance.save()
            movie_instance.actors.set(actors)
            return UpdateMovie(ok=ok, movie=movie_instance)
        return UpdateMovie(ok=ok, movie=None)

Como actores de referencia de películas, tenemos que recuperar los datos del actor de la base de datos antes de guardar. El bucle for primero verifica que los actores proporcionados por el usuario estén realmente en la base de datos, si no, regresa sin guardar ningún dato.

Cuando trabajamos con relaciones de muchos a muchos en Django, solo podemos guardar los datos relacionados después de guardar nuestro objeto.

Es por eso que guardamos nuestra película con movie_instance.save() antes de configurar los actores con movie_instance.actors.set(actors).

Para completar nuestras mutaciones, creamos el tipo de mutación:

1
2
3
4
5
class Mutation(graphene.ObjectType):
    create_actor = CreateActor.Field()
    update_actor = UpdateActor.Field()
    create_movie = CreateMovie.Field()
    update_movie = UpdateMovie.Field()

Hacer el esquema

Como antes, cuando diseñamos nuestro esquema, asignamos las consultas y mutaciones a la API de nuestra aplicación. Agrega esto al final de schema.py:

1
schema = graphene.Schema(query=Query, mutation=Mutation)

Registro del esquema en el proyecto

Para que nuestra API funcione, debemos hacer que un esquema esté disponible en todo el proyecto.

Cree un nuevo archivo schema.py en django_graphql_movies/ y agregue lo siguiente:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import graphene
import django_graphql_movies.movies.schema

class Query(django_graphql_movies.movies.schema.Query, graphene.ObjectType):
    # This class will inherit from multiple Queries
    # as we begin to add more apps to our project
    pass

class Mutation(django_graphql_movies.movies.schema.Mutation, graphene.ObjectType):
    # This class will inherit from multiple Queries
    # as we begin to add more apps to our project
    pass

schema = graphene.Schema(query=Query, mutation=Mutation)

Desde aquí podemos registrar el grafeno y decirle que use nuestro esquema.

Abra django_graphql_movies/settings.py y agregue 'graphene_django', como el primer elemento en INSTALLED_APPS.

En el mismo archivo, agregue el siguiente código un par de líneas nuevas debajo de INSTALLED_APPS:

1
2
3
GRAPHENE = {
    'SCHEMA': 'django_graphql_movies.schema.schema'
}

Las API de GraphQL se alcanzan a través de un punto final, /graphql. Necesitamos registrar esa ruta, o más bien ver, en Django.

Abra django_graphql_movies/urls.py y cambie el contenido del archivo a:

1
2
3
4
5
6
7
8
9
from django.contrib import admin
from django.urls import path
from graphene_django.views import GraphQLView
from django_graphql_movies.schema import schema

urlpatterns = [
    path('admin/', admin.site.urls),
    path('graphql/', GraphQLView.as_view(graphiql=True)),
]

Probando nuestra API

Para probar nuestra API, ejecutemos el proyecto y luego vayamos al punto final de GraphQL. En el tipo de terminal:

1
$ python manage.py runserver

Una vez que su servidor esté funcionando, diríjase a http://127.0.0.1:8000/graphql/. Encontrará GraphiQL, ¡un IDE integrado para ejecutar sus consultas!

Escribir consultas

Para nuestra primera consulta, obtengamos todos los actores en nuestra base de datos. En el panel superior izquierdo, ingrese lo siguiente:

1
2
3
4
5
6
query getActors {
  actors {
    id
    name
  }
}

Este es el formato para una consulta en GraphQL. Comenzamos con la palabra clave query, seguida de un nombre opcional para la consulta. Es una buena práctica dar un nombre a las consultas, ya que ayuda con el registro y la depuración. GraphQL también nos permite especificar los campos que queremos: elegimos id y name.

Aunque solo tenemos una película en nuestros datos de prueba, probemos la consulta película y descubramos otra gran característica de GraphQL:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
query getMovie {
  movie(id: 1) {
    id
    title
    actors {
      id
      name
    }
  }
}

La consulta película requiere una identificación, por lo que proporcionamos una entre paréntesis. Lo interesante viene con el campo actores. En nuestro modelo de Django, incluimos la propiedad actors en nuestra clase Movie y especificamos una relación de muchos a muchos entre ellos. Esto nos permite recuperar todas las propiedades de un tipo Actor que está relacionado con los datos de la película.

¡Este recorrido de datos similar a un gráfico es una de las principales razones por las que GraphQL se considera una tecnología poderosa y emocionante!

Mutaciones de escritura {#mutaciones de escritura}

Las mutaciones siguen un estilo similar al de las consultas. Agreguemos un actor a nuestra base de datos:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
mutation createActor {
  createActor(input: {
    name: "Tom Hanks"
  }) {
    ok
    actor {
      id
      name
    }
  }
}

Observe cómo el parámetro input se corresponde con las propiedades input de las clases Argumentos que creamos anteriormente.

También tenga en cuenta cómo los valores devueltos ok y actor se asignan a las propiedades de clase de la mutación CreateActor.

Ahora podemos agregar una película en la que actuó Tom Hanks:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
mutation createMovie {
  createMovie(input: {
    title: "Cast Away",
    actors: [
      {
        id: 3
      }
    ]
    year: 1999
  }) {
    ok
    movie{
      id
      title
      actors {
        id
        name
      }
      year
    }
  }
}

Desafortunadamente, acabamos de cometer un error. "Cast Away" salió en el año 2000!

Ejecutemos una consulta de actualización para solucionarlo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
mutation updateMovie {
  updateMovie(id: 2, input: {
    title: "Cast Away",
    actors: [
      {
        id: 3
      }
    ]
    year: 2000
  }) {
    ok
    movie{
      id
      title
      actors {
        id
        name
      }
      year
    }
  }
}

¡Ahí, todo arreglado!

Comunicación por correo postal {#comunicación por correo postal}

GraphiQL es muy útil durante el desarrollo, pero es una práctica estándar deshabilitar esa vista en producción, ya que puede permitir que un desarrollador externo tenga demasiada información sobre la API.

Para deshabilitar GraphiQL, simplemente edite django_graphql_movies/urls.py de modo que path('graphql/', GraphQLView.as_view(graphiql=True)), se convierta en path('graphql/', GraphQLView.as_view(graphiql=False) )),.

Una aplicación que se comunique con su API enviaría solicitudes POST al punto final /graphql. Antes de que podamos realizar solicitudes POST desde fuera del sitio de Django, debemos cambiar django_graphql_movies/urls.py:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
from django.contrib import admin
from django.urls import path
from graphene_django.views import GraphQLView
from django_graphql_movies.schema import schema
from django.views.decorators.csrf import csrf_exempt # New library

urlpatterns = [
    path('admin/', admin.site.urls),
    path('graphql/', csrf_exempt(GraphQLView.as_view(graphiql=True))),
]

Django viene integrado con protección CSRF (Cross-Site Request Forgery) - tiene medidas para prevenir usuarios autenticados incorrectamente del sitio de realizar acciones potencialmente maliciosas.

Si bien esta es una protección útil, evitaría que las aplicaciones externas se comuniquen con la API. Debe considerar otras formas de autenticación si pone su aplicación en producción.

En su terminal ingrese lo siguiente para obtener todos los actores:

1
2
3
4
5
$ curl \
  -X POST \
  -H "Content-Type: application/json" \
  --data '{ "query": "{ actors { name } }" }' \
  http://127.0.0.1:8000/graphql/

Tu deberías recibir:

1
{"data":{"actors":[{"name":"Michael B. Jordan"},{"name":"Sylvester Stallone"},{"name":"Tom Hanks"}]}}

Conclusión

GraphQL es un lenguaje de consulta fuertemente tipado que ayuda a crear API evolutivas. Diseñamos un esquema de API para películas, creando los tipos, consultas y mutaciones necesarias para obtener y cambiar datos.

Con Graphene podemos usar Django para crear API GraphQL. Implementamos el esquema de película que diseñamos anteriormente y lo probamos mediante consultas GraphQL a través de GraphiQL y una solicitud POST estándar.

Si desea ver el código fuente de la aplicación completa, puede encontrarlo aquí.