Collectors

Un collector résout un paramètre de handler par son type à partir du contexte d'invocation. Les opérations qui déclarent ce paramètre reçoivent la valeur automatiquement.

import { Collector } from '@fougere/core';
import type { InvocationContext } from '@fougere/core';
import User from '../../user/entities/User.js';

/**
 * Le middleware auth pose l'user de session sur ctx.state.user —
 * ceci le fait remonter à tout handler qui déclare `user: User | null`.
 */
export default class CurrentUserCollector extends Collector(User) {
  async collect(ctx: InvocationContext) {
    return (ctx.state.user ?? null) as User | null;
  }
}

Dès lors, dans n'importe quel handler de la Frond :

async mine(user: User | null): Promise<Post[]> {}
async publish(id: string, user: User | null): Promise<Post> {}

Règles de résolution

  • Le match est par nom de type d'entité : Collector(User) résout les paramètres typés User | null (ou User). La classe s'enregistre comme UserCollector par convention.
  • Épelez l'union. Le parser de signatures est AST-only : user: User | null matche, un alias de type (user: CurrentUser) est invisible et ne lie rien.
  • Les collectors sont des classes injectables — déclarez des dépendances de constructeur comme d'habitude.

Le state et le gradient

ctx.state est l'état de requête construit par l'application consommatrice, par exemple à partir de la session. Lors d'un appel distant, cet état est transmis avec l'invocation : le collector reçoit le même ctx.state.user qu'en local.

Un collector ne franchit pas la frontière d'une Frond

Placez le collector dans chaque Frond qui le consomme. Ce qui arrive sinon mérite d'être dit précisément, parce que ce n'est ni « plus tard » ni « rien » :

  • ce n'est pas au moment du split. La liaison est décidée au démarrage, dans un seul processus, à partir des collectors de la Frond elle-même ;
  • le paramètre n'est pas vide. Un type que la Frond ne sait pas résoudre tombe dans la quatrième règle de binding, body. Le handler reçoit donc le corps de la requête là où il attend un User.

Autrement dit, user: User | null déclaré dans une Frond qui n'a pas de UserCollector reçoit ce que le client a envoyé. Un handler qui juge sur user.role juge sur une valeur fournie par l'appelant.

Une version précédente de cette page disait que le collector était « perdu après une séparation de processus ». C'était faux deux fois, et la réalité est moins confortable.

Suite : Erreurs — les erreurs typées entre les couches.

Construit avec Fougere — ce site tourne sur le framework qu'il documente.