Gravity
par Matthieu
33' du 14 janvier 2022
Une alternative à Nest + GraphQL + Apollo
Plan de la présentation
-
Qu'est-ce que Gravity ?
-
Comparaison avec Nest + GraphQL + Apollo
-
Comment ça marche ?
-
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