Meteor est une plateforme js fullstack qui fait sa vie depuis bientôt 10 ans avec une communauté plutôt active. Son principal avantage est son approche reactive, Meteor.js se veut être une plateforme temps réel. Il accepte la syntaxe Typescript et permet de coder les vues en React depuis quelques années déjà. Que demander de mieux ?
Meteor.js s'appuie sur Node.js et intègre nativement plusieurs fonctionnalités et librairies, tels que MongoDB, les sockets, etc...
Il s'agit d'une plateforme fullstack qui permet de coder à la fois le front et le back dans des fichiers partagés qui peuvent être importé de n'importe où. C'est pourquoi il faut souvent vérifier dans le code où est-ce que l'on se positionne avant d'exécuter un code uniquement backend ou uniquement frontend.
Pour savoir si le code est exécuté coté client on fait :
import { Meteor } from 'meteor/meteor'
if (Meteor.isClient) {
// ... code exécuté uniquement sur le client
}
Pour le serveur, ce serait :
import { Meteor } from 'meteor/meteor'
if (Meteor.isServer) {
// ... code exécuté uniquement sur le serveur
}
Meteor fait communiquer ces deux briques via ce qu'il appelle le protocole DDP (pour Distributed Data Protocol) qui utilise à la fois :
Comprenez bien que ce protocole cadre énormément les choses par la suite !
Cela signifie que globalement vous ne ferez pas de REST API (même si vous pouvez en faire via des extensions), de même vous ne ferez pas de GraphQL (même si vous pouvez en faire via des extensions 😅).
Ainsi cela implique moins d'intervention sur la partie routing du projet, l'organisation se fera ailleurs via :
Autre plateforme, autre méthodo, je vous propose mon approche lorsque je travaille sur un projet Meteor. Cela demande de l'organisation afin de ne pas perdre ses repaires car nous sortons des sentiers battus. Dans cet article, je ne mentionnerai pas la définition des types qui doit être faite en amont (la base de Typescript !).
On défini notre collection Mongo :
// /imports/api/products.ts
import { Meteor } from 'meteor/meteor'
import { Mongo } from 'meteor/mongo'
export const ProductCollection = new Mongo.Collection<Product>('products')
J'utilise le fichier index pour centraliser tous les modèles et permettre de faire un import unique sur mon module serveur plus tard.
// /imports/api/index.ts
// On exporte nos modèles ici
// ....
export { ProductCollection } from './products'
Il est temps d'exposer nos données sur le frontend, mais ceci ne se fait pas sans une sécurité préalable ! Ici nous exposerons uniquement les produits appartenant à une même compagnie sur le frontend. Ce filtre sera appliqué par défaut pour toutes les requêtes DB qui arrivent du client. De même si l'on ajoute des conditions supplémentaires, elles viendront s'ajouter à celles existantes sur ce qui a été publié.
Par exemple : companyId = 'ABCD' + name = 'clé usb'
Idem pour la pagination, les skip etc...
// /imports/api/published/product-publ.ts
import { Meteor } from 'meteor/meteor'
import { ProductCollection } from '../product'
Meteor.publish('products', function (companyId: string) {
if (!companyId) return null
return ProductCollection.find({
companyId
})
})
Le fichier index du dossier published permet de centraliser tous les sous-modules :
// /imports/api/published/index.ts
import './product-publ'
Enfin nous importons tout cela dans notre Meteor server :
// /server/main.ts
import '/imports/api/published'
import '/imports/api'
Lorsque j'utilise Meteor, je gère les pages avec un react-router traditionnel. Mais j'organise toujours mes pages de manière à exporter le composant avec un Higher Order Component (appelé aussi HoC) qui permet de lier la partie vue à la couche de données et de souscrire aux événements (à la manière d'un redux connect).
Pratique pour le côté temps réel 👌!
// /imports/pages/products.tsx
import * as React from 'react'
import { useFind, useSubscribe, withTracker } from 'meteor/react-meteor-data'
import { ProductCollection } from '/imports/api' // On expose l'objet Mongo sur le front
type ProductsPageProps = {
products: Products[]
}
function ProductsPage({ products }: ProductsPageProps) {
return (
<table>
<thead>
<tr>
<td>Name</td>
<td>Price</td>
</tr>
</thead>
<tbody>
{
products.map((product: Product, index: number) => {
return <tr key={index}>
<td>{product.name}</td>
<td>{product.price} €</td>
</tr>
})
}
</tbody>
</table>
)
}
// Mapping avec les données
export default withTracker(() => {
// Hook de récupération de ma société (cookie ou session ou localStorage...)
const { activeCompany } = useCompany()
// On souscrit aux produits pour pouvoir les requêter
const isLoading = useSubscribe('products', activeCompany)
const products = useFind(
() => ProductCollection.find({}),
[ ]
)
return {
products
}
})(ProductsPage)
Toujours dans notre fichier d'api, pour la partie serveur, nous allons ajouter une méthode Meteor afin d'insérer la donnée en base :
// /imports/api/products.tsx
import { Meteor } from 'meteor/meteor'
import { Mongo } from 'meteor/mongo'
export const ProductCollection = new Mongo.Collection<Product>('products')
Meteor.methods({
'product.create'(companyId: string, data: Product) {
try {
return ProductCollection.insert({
...data,
companyId
})
} catch (err) {
console.log(err)
}
}
})
Côté vue, on se crée une page de formulaire. Personnellement j'utilise react-hook-form, mais je reviendrai un peu plus en détail sur cette librairie dans un prochain article.
// /imports/pages/product-add.tsx
import * as React from 'react'
import { useSubscribe, withTracker } from 'meteor/react-meteor-data'
import { useNavigate, useParams } from 'react-router-dom'
type ProductAddPageProps = {
onSubmit: (data: Partial<Product>) => void
}
function ProductAddPage({ onSubmit }: ProductAddPageProps) {
// ... formulaire sur lequel j'attache mon submit
})
export default withTracker(() => {
const navigate = useNavigate()
const { activeCompany } = useCompany()
return {
onSubmit: (data: Product) => {
Meteor.call('product.create', activeCompany, data, (err: any, productId: string) => {
alert('Product inserted')
navigate('/products')
})
}
}
})(ProductAddPage)
A ce stade vous savez faire de la lecture/écriture en base avec Meteor.js React et Typescript.
Dans de prochains articles nous verrons comment gérer les uploads et la pagination de données. Stay tuned ! 😃
Développeur passionné depuis plus de 20 ans, j'ai une appétence particulière pour les défis techniques et changer de technologie ne me fait pas froid aux yeux.
Nous utilisons des cookies sur ce site pour améliorer votre expérience d'utilisateur.