As a summary of our CRUD journey, we will work on creating the app for movies and celebrities. The goal of this exercise is to practice CRUD on at least one of the models (building the full CRUD for the movie model is mandatory) and documents relationships (between the two models). So let's see what are some user stories related to the celebrity model:
- The user should be able to:
1. Add new celebrities
2. See the list of celebrities
In the second part of the application, when you already have a couple of celebrities in your database, let's figure out what we will do regarding movie model. As we said, we will have a full CRUD on this model, which means the user can:
3. Add new movies
4. See the list of all movies
5. See the details of a specific movie, including its cast of celebrities
6. Update existing movies
7. Delete movies
Now that we know the overview of the app, let's proceed to creating it.
Hint: Although this lab might seem as overly guided, it is the first time you are creating a full-stack app on your own so we wanted to make sure all steps are covered and you can come back to this lab's solution as a reference at any point later.
Let's go!
- Fork this repo
- Clone this repo
- Upon completion, run the following commands:
$ git add .
$ git commit -m "done"
$ git push origin master
- Create Pull Request so your TAs can check up your work.
After forking and cloning the project, you will have to add an .env
file and add in it the following line:
PORT=3000
And you have to install all the dependencies:
$ npm install
Run the app and you are ready to start 🚀
In order to have everything organized, we will first create a couple of folders and files.
- Routes: In our
routes
folder, let's create separate files for our celebrities and movies. The naming is up to you, but we will use the following:routes/celebrities.routes.js
androutes/movies.routes.js
. You can add below starter router code to both of them and remember to link these two new files to eitherapp.js
orroutes/index.js
so your server has access to them.
// starter code in both routes/celebrities.routes.js and routes/movies.routes.js
const router = require("express").Router();
// all your routes here
module.exports = router;
-
Views: To keep things nice and clean, we will also create separate folders for
celebrities
andmovies
:views/celebrities
andviews/movies
. Also, we will create a couple of files in each folder:-
views/celebrities/celebrities.hbs
-
views/celebrities/new-celebrity.hbs
-
views/movies/movies.hbs
-
views/movies/new-movie.hbs
-
views/movies/movie-details.hbs
-
views/movies/edit-movie.hbs
-
Obviously, naming is the matter of preference so we used very descriptive names for routes and views.
Our first step is to create the Celebrity
model and add some celebrities in our database.
The Celebrity
model should have:
name
- String (like Tom Cruise, Beyonce, Daffy Duck, etc.)occupation
- String (what the celebrity does, why they are famous. For example actor, singer, comedian, or you can put unknown if your celebrity is someone like Kim Kardashian)catchPhrase
- String (every celebrity needs a good catch phrase. Well maybe not all of them have one in real life, but all of our celebrities will have a catch phrase. This can be pretty much anything.)
Go ahead and locate the Celebrity.model.js
model file in the models
folder. Using schema, create the Celebrity
model with the above mentioned properties. Don't forget to export the model. 2. In the Celebrity.model.js
model file:
Now that we have defined Celebrity model, let's make it so the user can add new celebrities to the database.
Route | HTTP Verb | Description |
---|---|---|
/celebrities/create |
GET | Show a form to create a celebrity |
/celebrities/create |
POST | Send the data from the form to this route to create the celebrity and save it to the database |
- In the routes file (
routes/celebrities.routes.js
) create the following GET route:/celebrities/create
- In that route we have to render the
celebrities/new-celebrity
view - In that view file:
- Add an
<h2>
for the page's heading. - Add a
<form>
tag that makes a POST request to/celebrities/create
. - Add
<input>
tags inside the form so the user can fill in values for each attribute of the celebrity. Make an input forname
,occupation
, andcatchPhrase
- Add a
<button>
tag in the form so the user can submit the form once they are done filling it out.
- Add an
- Create the
/celebrities/create
POST route inroutes/celebrities.routes.js
. - In that route we have to create an instance of the
Celebrity
model (don't forget, we should get all the info from the form through req.body)- If there is an error, render the
celebrities/new-celebrity
view so the user can try again and - If there is no error, redirect to the page with the list of celebrities. This route will be created in the next iteration
/celebrities
- If there is an error, render the
- In the
views/index.hbs
view file:- Add a link that goes to the page you just created with the form to create a new celebrity.
Now, when we've got some celebrities in the database, we can start working with them in our Express app. Let's display a list of all the celebrities.
Here's the route we will be using:
Route | HTTP Verb | Description |
---|---|---|
/celebrities |
GET | Show all celebrities |
- Create the
/celebrities
GET route inroutes/celebrities.routes.js
. - In the route:
- Use
find()
method on theCelebrity
model to retrieve all the celebrities - If everything is okay, render the
celebrities/celebrities.hbs
view and pass the array of celebrities into the view as a variable - If there's an error, catch it
- Use
- In the
views/celebrities/celebrities.hbs
view file:- Add an
<h2>
tag for the page's heading. - Use a hbs
#each
loop to display tags with each celebrity'sname
.
- Add an
- In the
views/index.hbs
(homepage) file:- Add a link that goes to the
/celebrities
route.
- Add a link that goes to the
Celebrities - Done! At least for now. 😉
Now when we've started all this good work, let's keep up strong and build all the routes for the Movie model. But first, let's create the Movie model.
The Movie
model should have:
title
- Stringgenre
- Stringplot
- Stringcast
- Array of object IDs referencing the Celebrity model (basically, the array of celebrities' IDs)
Go back and review what you did to create the Celebrity
model. You'll need to create a file for the model, and in that file, you'll need to create a schema for the model as well. Don't forget, you have to export the Movie
model.
Okay, the next step is to make it so the user can add new movies to the database.
Route | HTTP Verb | Description |
---|---|---|
/movies/create |
GET | Show a form to create a movie |
/movies/create |
POST | Send the data from the form to this route to create the movie and save it to the database |
Review how you did this for the Celebrity
model.
- Create 2 new routes, one to render page with the form on it, and one to send the data to after the form is filled out
- In the GET route that displays the form to create a new movie (which renders the
movies/new-movie.hbs
), make sure you pass all the celebrities from your database so your users can choose which ones are in the cast of the movie you're just creating (hint: You will have to use select multiple tag)
- In the GET route that displays the form to create a new movie (which renders the
- Remember that the user should see the cast name in the option tags, but the information that should be transmitted (
value
) is the_id
we will use for thecast
attribute of the movie. - Make sure the form is making a POST request to the other route you just created (
/movies/create
) - In your post route, create an object with all the info you just received from the form. (Remember,
req.body
) - Use this object to create a new movie in the database and redirect back to the page with your list of all movies
- Make sure to add a link to the form on the movies index page so the user can easier navigate
Now that we've got some movies in the database, let's make a page where we list all our movies, just like we did with the Celebrity
model.
Here's the route we will be using:
Route | HTTP Verb | Description |
---|---|---|
/movies |
GET | Show all movies |
Go back and review how you did this for the celebrities
. You'll need to:
- Create a GET route that will render the file in which we will display movies (
movies/movies.hbs
) - Use a database query to retrieve all the movies from your database and render the view
- Use a hbs
#each
loop to display all your movie titles on that page - Add a link to the page you just created on the home page so the user can navigate to it.
We've got a list of all movies that displays each of their titles, but what if we want to see the other details? In our movies/movies.hbs
view with our list of movies, let's add links so that the user can click on any movie's title, and go to a details page of each movie. On this page, we will show all the details of that movie.
Here's the route we will be using:
Route | HTTP Verb | Description |
---|---|---|
/movies/:id |
GET | Show a specific movie |
- We need
/:id
part to change dynamically as we click on different movies' titles. This being said, as part of the loop that displays each movie's title, add a link that goes to the/movies/:id
route with the:id
replaced by the actual movie's id 🔑 - Create the
/movies/:id
GET route inroutes/movies.routes.js
. - In the route:
- On the
Movie
model callfindOne()
orfindById()
method to retrieve the details of a specific movie by itsid
- Don't forget you have
cast
as the array of celebrityid
s, and we need topopulate()
in order to get the full data about the celebrities 🎯
- Don't forget you have
- If everything is fine (.then()), render the
movies/movie-details
view and pass the variable with the movie's details into the view - If there's an error, catch it.
- On the
- In the
views/movies/movie-details.hbs
view file:- Add an
<h2>
for the page's heading. - Display tags with the movie's
title
,genre
andplot
. - Use a hbs
#each
loop to display the cast'sname
,occupation
andcatchPhrase
- Add an
Now that we have a list of movies, a movie details page, and a page to create new movies, we only have 2 features left to implement: editing movies and deleting them. Since deleting is simpler, let's start with that.
Route | HTTP Verb | Description |
---|---|---|
/movies/:id/delete |
POST | Delete a specific movie |
- In the
movies/movie-details.hbs
file:- Add a
<form>
tag that makes a POST request to/movies/:id/delete
where the:id
is replaced by the actualid
of the movie. - Add a
<button>
tag inside the form so that it can be submitted.
- Add a
- Create the
/movies/:id/delete
POST route in yourroutes/movies.routes.js
file - In the route:
- Use the
Movie
model'sfindByIdAndRemove()
method to delete the specific movie by itsid
. - If everything is good (
.then()
), redirect to the list of movies page - If there's an error, catch it
- Use the
Final piece of our CRUD puzzle: editing existing movies.
Here are the routes we will be using:
Route | HTTP Verb | Description |
---|---|---|
/movies/:id/edit |
GET | Show a form to edit a movie |
/movies/:id/edit |
POST | Send the data from the form to this route to update the specific movie |
- Create the
/movies/:id/edit
GET route inroutes/movies.routes.js
. - In that route:
- Call the
Movie
model’sfindOne()
orfindById()
method to retrieve a specific movie by its id - Call the
Celebrity
model'sfind()
to retrieve all celebrities for the cast. - If everything is good, render the
movies/edit-movie
view - Pass the variable with the movie's details and all celebrities into the view
- Call the
- In the
movies/edit-movie.hbs
view file:- Add an
<h2>
tag for the page's heading. - Add a
<form>
tag that makes a POST request to/movies/:id
with the:id
replaced by the actual movie's id. - Add
<input>
tags inside the form for each attribute of the movie.- Hint: When you render the edit form, make sure each of the input fields is pre-filled with the current value of the attribute for that movie
- Add
<select>
and<option>
tags that will handle the cast attribute. - BONUS: Make the current cast members selected so the user knows who is in the cast currently.
- Add a
<button>
tag inside the form so that the user can submit the form once they are done editing.
- Add an
- Create
/movies/:id
POST route in theroutes/movies.routes.js
file - In that route:
- Create an object with movie's model keys and it's values should come from the form submission (which is
req.body
) - Now you can apply different methods -
update()
orfindByIdAndUpdate()
to find the movie and send the updated values to the database. - If there is no error, redirect back to the movie details page.
- Create an object with movie's model keys and it's values should come from the form submission (which is
And we are done! Now all movies
CRUD features are implemented with a relationship between movies
and celebrities
. As a BONUS, feel free to also add details view, edit and delete for the celebrities
.
- See the details of a specific celebrity
- Update existing celebrities
- Delete celebrities
That's it! 🏆
Happy Coding! ❤️