Creating API clients that are "async agnostic"
The code is admirably clear.
Nice annotations.
With these classes,
Chuck Norris wouldn't need a debugger,
he could just stare down the bug until the code confesses.

class Request(NamedTuple):
    method: str


Maybe list method 2nd, and allow defaulting to 'GET'?

    response = yield Request('GET', URL_TEMPLATE.format(id=id))

    try:

        if response.status != 200:


Ok, nit-picky observation: there's more than one success code,
so one might bury the test in an is_success() helper function.
(I use an API that routinely returns 204 success.)
But this is for pedagogic purposes, and the 200 test is perfect, don't change it.

Broader observation: showing that "stuff which can go wrong after GET" happens
within the try is pretty interesting.
There is a small amount of "ceremony" to access the result.
If this were a production library, rather than pedagogic, I'd be wondering
if we could maybe save clients from looking for 200 status at all,
and just raise appropriate errors if client is asking for bad data.

This would probably need an url_prefix_to_api_name registry map,
so we could identify a given url as being a Joke url,
and could look for a class with the conventional name JokeApiError.
(Clearly the registry would never contain Norris, because:
No statement can catch the ChuckNorrisException.)

The next few lines have boilerplate checks that maybe could be defaulted,
and / or moved into some JokeApi method a user could choose to supply.

The raise in this line:

    raise Return(data['value']['joke'])


is, ummm, slightly surprising.
I'm sure you have your technical reasons for it.
But maybe it could be a return, or a yield, or simply a method call with side effects?
Or a .close()-style cleanup handler of a with resource manager?
Or simply buried in library code that developers typically won't view?
The raise is just a little jarring, given that this is the success case.

I encourage you to keep pushing on this.
Two ways to win:


Write something a little bigger than just joke access, maybe do some CRUD.
Convince one other developer to use your stuff.


Both will push your code in the direction of increased maturity,
increased readiness for a 1.0 release.