Virtual en action !

🌱Idée sous-jacente

Pour Mongoose : les virtuals sont utilisés pour simuler des relations entre entités, notamment avec populate et les mécanismes de lookup côté MongoDB.


  • Virtual = champ calculé, non stocké en base

  • Virtual populate = relation déclarative

🎯But

Le but est de séparer logique et persistance

- Le modèle MongoDB reste simple (sans duplication ou clé étrangère obligatoire).

-  Les entités sont reliées au niveau applicatif, via Mongoose, ce qui rend le code plus expressif et maintenable.


🆘Explication

 lecture  : → virtual


Virtual est au coeur de mongoose ! 

Autres exemples.


🆘Le coût d'une pizza : champ calculé non stocké.
  1. import mongoose from "mongoose";

  2. const pizzaSchema = new mongoose.Schema({
  3.   name: { type: String, required: true },
  4.   // `description` stores a simple recipe/notes string (renamed from `recept`)
  5.   description: { type: String, default: "" },
  6. }); 

  7. // Include virtuals when converting documents to objects/JSON
  8. pizzaSchema.set("toObject", { virtuals: true });
  9. pizzaSchema.set("toJSON", { virtuals: true });

  10. // Virtual populate: connect Pizza -> Topping via Topping.pizzas (inverse relation)
  11. pizzaSchema.virtual("toppings", {
  12.   ref: "Topping",
  13.   localField: "_id",
  14.   foreignField: "pizzas",
  15.   justOne: false,
  16. });

  17. // Virtual total price (cents) includes toppings when `toppings` is populated
  18. pizzaSchema.virtual("totalPriceCents").get(function () {
  19.   // Compute total price from populated `toppings` only (no base pizza price)
  20.   if (
  21.     !this.toppings ||
  22.     !Array.isArray(this.toppings) ||
  23.     this.toppings.length === 0
  24.   )
  25.     return 0;
  26.   return this.toppings.reduce((acc, t) => acc + (t.priceCents || 0), 0);
  27. });

  28. pizzaSchema.virtual("totalPriceEur").get(function () {
  29.   return (this.totalPriceCents || 0) / 100;
  30. });

  31. export const Pizza = mongoose.model("Pizza", pizzaSchema);
🆘relation inverse ! 

Soit un schéma topping : 

const toppingSchema = new mongoose.Schema({
  title: { },
  pizzas: [{ type: mongoose.Schema.Types.ObjectId, ref: "Pizza" }],
});

pizzas = ensemble des pizzas qui a cet ingrédients.

🛟Comment trouver les pizzas qui ont un ingredient ?

→ Dans le schéma pizza on ecrit un virtual !

🥷pizzaSchema.virtual("toppings", { ref: "Topping", localField: "_id", foreignField: "pizzas", justOne: false, });

Ici on crée une relation inverse :

  • ref: "Topping" → référence le modèle Topping.
  • localField: "_id" → utilise l’ID de la pizza.
  • foreignField: "pizzas" → cherche dans le champ pizzas des documents Topping (qui doit être un tableau d’IDs de pizzas).
  • justOne: false → une pizza peut avoir plusieurs toppings.

👉 En clair : ce virtual permet de récupérer tous les Topping qui contiennent l’ID de cette pizza dans leur champ pizzas.


without populate

export async function getBooks(req, res) {
  const books = await Book.find().populate("authors");
  res.json(books);
}

What happens without populate: book.authors is an array of ObjectId strings. The UI must resolve those ids to author details itself (or ask the server to).

Naive client approach: map ids -> N requests (one per author). Works but is slow and causes N+1 requests.

Example (works, but not optimal):

// book.authors = ['691c6...', '691d...']
const names = await Promise.all(
  book.authors.map(id =>
    fetch(`/authors/${id}`).then(r => {
      if (!r.ok) throw new Error('author fetch failed');
      return r.json();
    }).then(a => a.name)
  )
);
book.authors = names; // replace ids with names

Better: batch on the server — add an endpoint that accepts multiple ids and returns the authors in one query:

// GET /authors?ids=691c6...,691d...
// Server (Express + Mongoose)
router.get('/authors', async (req, res) => {
  const ids = (req.query.ids || '').split(',').filter(Boolean);
  const authors = await Author.find({ _id: { $in: ids } }, 'name'); // only name field
  res.json(authors);
});

Client side:

const ids = book.authors.join(",");
const resp = await fetch(`/authors?ids=${encodeURIComponent(ids)}`);
const authors = await resp.json();
const map = new Map(authors.map((a) => [a._id, a.name]));
book.authors = book.authors.map((id) => map.get(id) || null);


Bilan : Keep using populate server-side so the UI gets names in one response.

  • Server-side aggregation to return books with only needed author fields.
  • populate('authors', 'name') server-side so the UI gets names in one response.

*Transformer APP en API

📦 Blog Posts API (HTML + JSON)

This project serves blog posts two ways:

  1. Traditional server-rendered HTML pages (SSR)
  2. A clean JSON API you can consume with fetch() from any frontend

The goal: show the difference between an "application" that returns HTML and an "API" that returns raw data — and let you easily build your own client.

Git 🚀 https://github.com/dupontdenis/mongoose-API-virtual.git


API UI (Read-Only)

A minimal, dependency-free web UI to view posts from a REST API.

Supported endpoints:

  • GET /api/posts — list all posts
  • GET /api/posts/:id — get one post by ID

Git 🚀https://github.com/dupontdenis/API-Viewer.git

Methodes et Virtual in shema

🎓 Mongoose Virtual & Method Mini Demo

Minimal teaching project to show two core Mongoose schema features:

  1. virtual property url (computed, not stored in MongoDB)
  2. An instance method getSummary(maxLength) (logic with parameters)

🚀 https://github.com/dupontdenis/mongoose-virtual.git

Agregation

Tiny Mongoose walkthrough that models a simple university example and demonstrates two ways to compute an average: an instance method on the document and a MongoDB aggregation pipeline.

git 🚀 https://github.com/dupontdenis/mongoose-agregation.git

API with mongoose !

 

🎯 Learning Objectives

  • Understand REST API principles
  • Learn HTTP methods: GET, POST, PUT, PATCH, DELETE
  • Work with Express.js and MVC pattern
  • Connect to MongoDB
  • Interact with the API using Fetch, curl, or the VS Code REST Client


git 🛩️ https://github.com/dupontdenis/let-s-start-mongoAPI.git


🚀Applications pour tester l'API !

 Pensez à bien lancer le serveur dans un terminal


Simple : https://dupontdenis.github.io/testAPI/


https://dupontdenis.github.io/useAPIBLOGSIMPLE/

Let's start with mongoose !

🎯 Learning Objectives

This repository is a **hands-on tutorial** designed to teach you the fundamentals of Mongoose, the popular MongoDB object modeling library for Node.js. Through practical examples, you'll learn how to: 

  • 📦 Connect to MongoDB databases
  • 📝 Create documents (INSERT operations)
  • 🔍 Read documents with various query methods (SELECT operations)
  • 🎨 Use schemas to structure your data
  • 🔧 Work with ES Modules in a modern Node.js environment

many to one !

git 🛩️

This repository is designed to teach you about two different ways to implement many-to-one relationships in MongoDB using Mongoose. We'll use a classic example: Authors and Books.

 https://github.com/dupontdenis/mongoose-one-two-many.git