Incremental Delivery🔗

Note that incremental delivery is currently experimental and may change in the future.

Undine has experimental support for incremental delivery of data using the @defer and @stream directives. To enable incremental delivery, all of the following must be true:

  1. graphql-core version must be 3.3.0a12 (note alpha version, later versions might not work)
  2. EXPERIMENTAL_INCREMENTAL_DELIVERY must be set to True
  3. Async support must be enabled

Let's look at an example of incremental delivery using the @defer and @stream directives. Given the following schema:

import asyncio
from collections.abc import AsyncIterable

from undine import Entrypoint, Field, GQLInfo, QueryType, RootType, create_schema

from .models import Task


class TaskType(QueryType[Task]):
    id = Field()
    name = Field()
    done = Field()

    @Field
    async def slow(self, info: GQLInfo) -> str:
        await asyncio.sleep(5)
        return "OK"

    @Field
    async def countdown(self, info: GQLInfo) -> AsyncIterable[int]:
        for index in range(10, -1, -1):
            await asyncio.sleep(1)
            yield index


class Query(RootType):
    tasks = Entrypoint(TaskType, many=True)


schema = create_schema(query=Query)

If we wanted to query the "slow" field, the client would need to wait for five seconds for the server to resolve it before it can show any of the other data.

1
2
3
4
5
6
7
8
query {
  tasks {
    id
    name
    done
    slow
  }
}

However, using the @defer directive, the client can receive the rest of the data immediately and the deferred data when its complete. The @defer directive works on fragment spreads an inline fragments.

query {
  tasks {
    id
    name
    done
    ... @defer {
      slow
    }
  }
}

Similarly, if we wanted to query the "countdown" field, the client would need to wait for ten seconds for the entire countdown to end before it can show any of the other data.

1
2
3
4
5
6
7
8
query {
  tasks {
    id
    name
    done
    countdown
  }
}

However, using the @stream directive, the client can receive the rest of the data immediately and stream in each countdown result as they become available. The @stream directive works on list fields.

1
2
3
4
5
6
7
8
query {
  tasks {
    id
    name
    done
    countdown @stream
  }
}