Gravity

par Matthieu

33' du 14 janvier 2022

Une alternative à Nest + GraphQL + Apollo

Plan de la présentation

  1. Qu'est-ce que Gravity ?

  2. Comparaison avec Nest + GraphQL + Apollo

  3. Comment ça marche ?

  4. Conclusion

Gravity : qu'est-ce que c'est ?

Gravity is a full-stack framework to provide an easy-to-use and scalable end-to-end typesafe API with an enjoyable developer experience.

Gravity est un framework RPC
(RPC = Remote procedure call)

Il permet au client "d'appeler" des fonctions du serveur

 🗺 Objectifs

• Être une alternative à la stack Nest + GraphQL + Apollo

• Améliorer l'expérience de développement

• Augmenter la productivité par un facteur de 2 à 3

Pourquoi "Gravity" ?

 ✨ Features

• 100% safe

• Orienté Typescript

• Pas de déclaration de schéma

• Système d'autorisation flexible

• Architecture modulaire et scalable (par "Services")

• Intégration facile avec d'autres frameworks (Svelte, React, Express, MikroORM, Prisma...)

• Simple à apprendre, facile à utiliser

• Pas de génération de code côté front

• Pas de distinction query / mutation

🔬 Petit example

/**
 * We define a service "math" that we will be exposed to our client
 */
export class math extends Service {
  
  add(a: number, b: number): number {
    return a + b
  }
}

Serveur

import { api } from "../api"

const response = await api.math.add(30, 12)

console.log("Response:", response) // will print "Response: 42"

Client

→  On invoque une fonction du serveur comme n'importe quelle fonction

→  Pas besoin d'apprendre un langage de query comme GraphQL

→  On bénéficie du typage fort de Typescript et de l'autocomplétion

Qualités de Nest et GraphQL

→  Nest est un des premiers frameworks back Typescript

→  Contrairement à Express, il propose une architecture modulaire scalable

→  GraphQL est un excellent language de query et de définition de schéma particulièrement adapté à la récupération de données depuis une base de données

Défauts de Nest + GraphQL

→  Inutilement compliqué (beaucoup de concepts)

→  Extrêmement verbeux

Ajoute beaucoup trop de décorateurs et augmente encore la verbosité

→  Introduit de la répétition de code  ⚠️

→  GraphQL a des limitations

Nest est...

L'intégration de GraphQL avec Nest...

→  Nécessite de la génération de code

(qui seront peut-être adressées dans le futur)

Limitations de GraphQL

→  Conversion par défaut des dates en strings

→  Impossible de travailler avec de gros entiers (≧ 2 ^ 32)

(cela oblige à créer des types custom)

→  Impossible de retourner un type `String | Number` 

(à moins de créer un type custom)

→  L'upload de fichiers est compliqué

→ Un seul immense namespace, ce qui force à utiliser des noms à rallonge

Augmenter la productivité par un facteur de 2 à 3... vraiment ?

🤔

Oui.

(Peut-être même plus.)

Plus de modules synthétiques "à la Angular"

→  Plus besoin de définir des modules

→  Plus de notion de "providers" et "d'injection"

→  Plus de

forwardRef(() => ...)

→  Plus de

forFeature([ ... ])

→  Plus besoin d'ajouter une surcouche d'imports / d'exports

Plus besoin de décorateurs

@InterfaceType
@InjectRepository
@Injectable
@Inject
@Module
@Resolver
@Query
@Mutation
@UseGuards
@ResolveField
@ResolveProperty
@Args
@ArgsType
@Parent
@ObjectType
@Field
@InputType
@Controller
@Post
@Get
@Delete

Plus de distinction resolver / service

→  90% du temps, les fonctions des resolvers appellent la fonction éponyme du service associé

→  Ce qui fait des resolvers une "vue" sur un service

→  Cette vue indique quelles fonctions du service sont exposées au client

→  Avec Gravity, il n'y a pas de resolvers

→  Les services exposent simplement des méthodes publiques ou privées

Example : "company.mutations.resolver.ts" du projet HappyPal

Plus de fichiers .dto

→  Les fichiers .dto servent à définir le type des fonctions  dans les resolvers

→  C'est pour permettre à GraphQL de valider les inputs et les outputs

→  Avec Gravity, il n'y a pas besoin de définir des dto

→  La signature Typescript des fonctions d'un service est réutilisée

Example : "company-memberships-pagination.dto.ts" du projet HappyPal

@InputType()
export class CompanyMembershipCreateInput {
  @Field(() => CompanyMembershipRole)
  @IsEnum(CompanyMembershipRole)
  role: CompanyMembershipRole;

  @Field(() => CompanyMembershipCreateInputUser)
  @ValidateNested()
  @Type(() => CompanyMembershipCreateInputUser)
  user: CompanyMembershipCreateInputUser;

  @Field(() => String, { nullable: true })
  @IsString()
  @IsOptional()
  worksite?: string | null;

  @Field(() => String, { nullable: true })
  @IsString()
  @IsOptional()
  jobTitle?: string | null;

  @Field(() => DateScalar, { nullable: true })
  @IsDate()
  @IsOptional()
  joinedAt?: Date | null;
}
export type CompanyMembershipCreateInput = {
  role: CompanyMembershipRole;
  user: CompanyMembershipCreateInputUser;
  worksite?: string | null;
  jobTitle?: string | null;
  contract?: string | null;
  joinedAt?: Date | null;
}

(Gravity)

Plus de répétition de code  ⚠️

Plus de génération de code

→  Côté front, il est nécessaire de générer un fichier Typescript pour chaque fichier GraphQL

→  Cela permet de correctement typer les requêtes et les réponses au serveur

→  Cette solution n'est pas scalable : plus le projet grandit, plus la génération de code est lente

Example : "myCompanyUpdateBlockDialog" du projet AIPMI

→  Cela complexifie et ralentit énormément le développement front (latence de la génération, imports supplémentaires, noms à rallonge)

Front

"MyCompanyUpdateBlocksDialogJobsUpdateUpdateCompanyJobsMutationVariables"

Plus d'outils vieillissants

Nest

Gravity

Démonstration

🔬

Ce qu'on va créer

Première partie : Mise en place

→  On part d'un projet squelette SvelteKit

→  Installation des dépendances

→  Mise en place côté back et côté front 

→  Création d'un service simple

→  Utilisation du service côté front

→  Utilisation du service avec un cache "à la Apollo"

Ce qu'on va créer

Deuxième partie : Concepts avancés

→  Mettre en place un système d'autorisation

→  Définir des guards

→  Connexion et intégration avec un ORM

onRequestSend

onRequestReceive

authorize

onResponseSend

onResponseReceive

Communication front ↔ back

Récapitulatif

Où en est le projet

Core features

✅ Server / client communication

✅ Context

✅ Use other services in service

✅ Low-level metadata API for services and operations

✅ Guard & tag decorators

✅ Gravity callbacks

✅ Error handling

🚧 Automatic schema generation

🚧 Parameters validation at runtime

🚧 Heavy tests

Où en est le projet

Integrations

✅ Express, Polka, h3, Connect, etc...

✅ SvelteKit

✅ Vanilla Node server

❌ Next.js

❌ Nuxt

✅ Svelte

❌ React

❌ Vue 3

🚧 Prisma

❌ MikroOrm

 

Fin de la démonstration

🥳

Merci d'avoir participé !

🌍

🌛

Gravity

By lonestone

Gravity

  • 270