---
tags: ironhack, lecture,
---
<style>
.markdown-body img[src$=".png"] {background-color:transparent;}
.alert-info.lecture,
.alert-success.lecture,
.alert-warning.lecture,
.alert-danger.lecture {
box-shadow:0 0 0 .5em rgba(64, 96, 85, 0.4); margin-top:20px;margin-bottom:20px;
position:relative;
ddisplay:none;
}
.alert-info.lecture:before,
.alert-success.lecture:before,
.alert-warning.lecture:before,
.alert-danger.lecture:before {
content:"đšâđ«\A"; white-space:pre-line;
display:block;margin-bottom:.5em;
/*position:absolute; right:0; top:0;
margin:3px;margin-right:7px;*/
}
b {
--color:yellow;
font-weight:500;
background:var(--color); box-shadow:0 0 0 .35em var(--color),0 0 0 .35em;
}
.skip {
opacity:.4;
}
</style>

# Mongoose&Express | Create - Update Documents
## Learning Goals
After this lesson, you will be able to:
- Create new documents on the database with data from a form filled by a user
- Update documents in the database
- Print prefilled form with info retrieved from the database
- Get the updated document when doing an `update` method using Mongoose.
## Introduction
:::info lecture
Dans nos 2 cours précédents, nous avons jusque maintenant listé (`/books`) et affiché (`/books/:id`) des documents de notre DB : C**R**UD.
---
Nous allons maintenant voir comment en créer et mettre à jour...
:::
Continuing our `library-project` example, integrating and mixing the concepts we learned during the second module, in the following learning unit we will see how we can create and update documents using Mongoose, MongoDB, and Express.
Create and update documents is an essential part of any web application, and for sure will be part of our second project.
For more straightforward setting up, we will use the same project we had build till now, so go ahead an open the folder! :wink:
## Setting up everything
:::info lecture
Ajoutons un livre...
:::
In our example, we will create new books and store them in our database.
:::info
**Discussion**
- Which routes you need to create to display a form where a user can fill the info, and then get the info and create a new book in the database?
- Which method will you use on each of those routes?
:::
Yes, we will need two routes, the first one for rendering the form where the user can fill all the info about a new book, and another one for getting the data about the book and add it to the database. So let's do it!
First, create a new route `books/add` on our `routes/index.js` file that should render a `book-add.hbs`. **Which method will we use?**
:::info lecture
Afin que l'utilisateur puisse ajouter lui-mĂȘme un livre, nous avons besoin de lui mettre Ă disposition un formulaire : crĂ©er donc la **route d'affichage du formulaire** :
:::
```javascript
// routes/index.js
router.get('/books/add', (req, res, next) => {
res.render("book-add");
});
```
:::warning lecture
â ïž de dĂ©finir les routes `/books/add` AVANT `/books/:bookid` !
:::
Now we need to add a form on the `book-add.hbs` file, with all the fields we need to create a new book.
:::info lecture
Et notre **template du formulaire** :
:::
```htmlmixed
{{! book-add.hbs }}
<form action="/books/add" method="POST">
<label for="">Title:</label>
<input type="text" name="title">
<label for="">Author:</label>
<input type="text" name="author">
<label for="">Description:</label>
<input type="text" name="description">
<label for="">Rate:</label>
<input type="number" name="rating">
<button>ADD</button>
</form>
```
:::info lecture
Enfin, la **route de traitement du formulaire** :
:::
And the second one? We need another to get the data and add to our Mongo database. We can use the same route but changing the method.
```javascript
// routes/index.js
router.post('/books/add', (req, res, next) => {
// TODO
});
```
## Create Documents
All setup! We are ready for creating new documents and store them in our database.
:::info lecture
Notre formulaire en place ainsi que notre route de traitement, voyons comment enregistrer cela en base...
:::
### Retrieve the data from a POST Request & Creating the Document
How can we retrieve the data in the `/books/add` route?
All the data will be included on the `body` property of the `request` object, so we can handle it to create a new book in the database, but let's do it step by step:
:::info
You need to include all the following inside our `books/add` route with the `POST` method!
:::
1. First, we need to retrieve the data and store in new variables. ES6 destructuring will help with that:
:::info lecture
Récupérons les valeurs saisies grùce à `req.body` :
:::
```javascript
const { title, author, description, rating } = req.body;
```
Remember this is possible only if the variables have the same name on the `req.body` that means they need to have the same name on the `name` attribute of each `input`.
2. We can create a new `Book` using the model we import. It's important to notice we are using some ES6 features that make our code looks much cleaner!
:::info lecture
Avec ces valeurs, créons une **nouvelle instance** de livre :
:::
```javascript
const newBook = new Book({ title, author, description, rating});
```
3. We can store it in the database, using the `save()` method. Since this is an asynchronous process, we need to process it as a `Promise`. If everything goes ok, we will receive the `book` we just save. Otherwise, we will have an error. We need to control both options!
:::info lecture
**Persistons** cette instance en base :
:::
```javascript
newBook.save()
.then((book) => {
})
.catch((error) => {
})
;
```
4. Finally, we can redirect our user to the `/books` view, where we list all the book we have in the database. The new book should be already there. We should have the following code on our route:
:::info lecture
Enfin :
- redirigeons si tout c'est bien passé
- ou affichons l'erreur Ă l'utilisateur grĂące Ă `next`
:::
```javascript
// routes/index.js
router.post('/books/add', (req, res, next) => {
const { title, author, description, rating } = req.body;
const newBook = new Book({ title, author, description, rating})
newBook.save()
.then((book) => {
res.redirect('/books'); // redirect to listing
})
.catch((err) => {
next(err); // display error
})
;
});
```
Awesome! Now we should see the new book we just created!
Creating documents is a super standard action on web applications, and you need to be sure you understand step by step all the process you will need to do it, so if anything is not clear enough, now it is the perfect time to ask! :wink:
:::info lecture
Testons tout cela et ajoutons livre !
:::
## Edit Documents
Any user can add books to our `library-project`, but what about modifying one? Let's add this feature to our project!
### Edit Form
:::info lecture
Afin que l'utilisateur puisse editer lui-mĂȘme un livre, nous devons lĂ encore lui mettre Ă disposition un formulaire d'Ă©dition : `views/book-edit.hbs`
:::
First, we need an edit form, where the user will be able to modify the info of each book. Let's create a `book-edit.hbs` view and add the following code:
```htmlmixed
{{! views/book-edit.hbs }}
<form action="/books/edit" method="post">
<label for="">Title:</label>
<input type="text" name="title">
<label for="">Author:</label>
<input type="text" name="author">
<label for="">Description:</label>
<input type="text" name="description">
<label for="">Rate:</label>
<input type="number" name="rating">
<button>EDIT</button>
</form>
```
:::warning lecture
Ici, tous **les champs seront vides** ! Nous voudrions au contraire qu'il soient **pré-remplis** avec leur valeur courante...
:::
**Is this enough to edit a book?** It is, but with the awfull user experience. Imagine you as a user clicking on an edit button and all the info about the element you are trying to edit is not there, so you have to fill all the fields again? That sucks right? So we need to set all the inputs value with prefilled values from the existing book.
#### How can we do that?
We need to create a route where we will render this view, but before we should pass the info about the book the user is trying to edit.
First, let's add the edit link to each of our books on the `/books` route.
**How can we pass the info about the book we are trying to edit?**
- *Route Params*. We can set a route like the following: `book/edit/:id` where we will receive the `id` as a `req.params`.
- *Query String*. Another option is to set the route: `book/edit` and pass the data as a query string using the `?`.
We choose the second option, but any of them is valid! :wink: Add the following code to our `books.hbs` file:
:::info lecture
Ajoutons tout d'abord le lien vers la page d'édition dans le listing des livres : L7
:::
```htmlmixed=
{{! views/books.hbs }}
<h1>BOOKS</h1>
{{#each books}}
<p>
<a href="book/{{this._id}}">{{this.title}}</a>
<a href="/books/edit?book_id={{this._id}}" class="edit-button">EDIT</a>
</p>
{{/each}}
```
:::warning lecture
Ici l'URL sera : `/books/edit?book_id=` (query string) mais nous aurions tout aussi bien pu décider d'utiliser les route-params
:::
:::info
Notice how we set the `href` attribute for editing the documents, this way the `book_id` property will be dynamic.
You can also add the following **css** to the `style.css` file, so we differentiate the **edit** button.
```css
.edit-button {
margin-left:20px;
color: #fff;
text-decoration: none;
padding: 2px 4px;
background-color: grey;
border-radius: 6px;
}
```
:::
### Get the data
We know where the user will go when clicking on the **Edit** button. We need to create the route to get that user `request` and render the view.
:::info lecture
Notre lien vers la page d'édition en place, occupons-nous de sa route :
:::
```javascript
router.get('/books/edit', (req, res, next) => {
res.render("book-edit");
});
```
But before rendering the **edit form**, we should retrieve the data of the book from our database and pass that data to our view. How can we get the data of the book we are clicking? Remember we are passing the `id` through the **query string**.
:::info lecture
Cependant, **afin de pouvoir pré-remplir les champs** du formulaire par les valeurs actuelles du document en question, nous devons passer ses info à la vue, et par conséquent au préalable les retrouver depuis la base :
:::
```javascript
router.get('/books/edit', (req, res, next) => {
//
// đ Retrieve book datas before rendering the edit form
//
Book.findOne({_id: req.query.book_id}) // đ we consume the query-string ?book_id=
.then((book) => {
res.render("book-edit", {
book: book // đ once our book retrieved, we pass it to the view
});
})
.catch((error) => {
next(err);
})
;
});
```
:::info lecture
Nous utilisons ici `req.query.book_id` du lien afin d'identifier quel livre fetcher depuis la base.
:::
We need the `req.query` object to get the `id` of the book and then query the database asking for all the info about it.
:::info
Notice we are using the `findOne` method, this way the database returns an object with the book we are looking for. If we use the `find` method, Mongoose retrieves an array of objects.
:::
#### Prefilled fields
We are now rendering our edit form, but they are empty. We need to fill those `input` with the info we retrieve from the database. Add the `value` attribute, with the corresponding info about each field.
:::info lecture
Les infos du livre en question ayant maintenant été passées à la vue, **nous pouvons pré-remplir chacun des champs** avec la valeur actuelle : grùce à l'attribut `value=""`
:::
```htmlmixed
{{! views/book-edit.hbs }}
<form action="/books/edit?book_id={{book._id}}" method="post">
<label for="">Title:</label>
<input type="text" name="title" value="{{book.title}}">
<label for="">Author:</label>
<input type="text" name="author" value="{{book.author}}">
<label for="">Description:</label>
<input type="text" name="description" value="{{book.description}}">
<label for="">Rate:</label>
<input type="number" name="rating" value="{{book.rating}}">
<button type="submit">EDIT</button>
</form>
```
Perfect! Now every input is prefilled with the info of the book we are trying to edit. We also modify the **action** attribute of the `form`. When the user clicks on the **EDIT** button, the web will make a `POST` request to that **URL**. Let's move forward!
:::info lecture
Testons l'affichage du formulaire d'édition...
:::
### Update the Document
:::info lecture
Ne nous **manque plus que la route de traitement** du formulaire, ie : la route appelée quand le formulaire sera soumis...
:::
Create the route with a `POST` method so we can get the info of the book. Inside the route, we should get all the info from the `req.body`, and the book `id` from the `req.query` and then use the `update` method to edit the book on our database.
:::info
Remember the syntax for the `update` method. The first parameter is the query to find the element we want to edit. On the second parameter, we specify the fields we want to update. Since we are getting all the fields from the `req.body` you can set all of them.
```javascript
Model.update({ query }, { $set : { key: value, key: value }})
.then()
.catch()
```
:::
On the `POST` method of the route you should have the following:
:::info lecture
Créons cette nouvelle route, correspondant à l'`action` du formulaire :
:::
```javascript=
// routes/index.js
router.post('/books/edit', (req, res, next) => {
const { title, author, description, rating } = req.body;
Book.update({_id: req.query.book_id}, { $set: {title, author, description, rating }})
.then((book) => {
res.redirect('/books');
})
.catch((error) => {
next(error);
})
;
});
```
:::info lecture
- L4 : récupération des valeurs saisies dans le form
- L6 : appel de `Book.update` avec les valeurs récupérées
:::
#### Get the updated Document
After updating the document, **Mongoose** returns us the old document from the database. And sometimes this can confuse us a bit because in some cases we want to pass to the view the updated document to print it. Fortunately, we can add a *third parameter* to the `update` method so Mongoose will return us the updated document. The third parameter should be an object we specify we want the *new* document: `{ new: true }`. The full syntax, looks like this:
:::warning lecture
le `book` retourné dans le `then` de l'update, sera l'ancienne version.
:::
```javascript
Model.update({ query }, { $set : { key: value, key: value }}, { new: true })
.then()
.catch()
```
## Summary
We just learned how we could create and edit documents on our website using Express & Mongoose. This two actions will be essential when doing our second project.
Every step we saw in this learning, we already learned before, but now we are mixing everything. You must be sure you understand every step, this way when you face by yourself this challenge you are ready to do it and customize to your web requirements.
## Extra Resources
- [Mongoose Create and Update](http://mongoosejs.com/docs/documents.html) - Official Docs