In this challenge (and the upcoming ones), you'll create a fish shop. For now, you'll read data from a local MongoDB using mongoose
and display it in the frontend.
Run npm run dev
and open localhost:3000
on your browser.
Have a look around:
- there is an overview page with all products and a details page for each of them;
- the data is taken from
lib/products.js
.
Your task is to refactor the app so that it fetches the data from a local MongoDB.
Use MongoDB Compass to create a database:
- the database should be called
fish-shop
, - there should be one collection called
products
, - download and extract the resources and
- use the
products.json
file to import the data into yourproducts
collection.
Create a schema for the Product
model in the db/models
folder.
The schema should have the following fields:
name
(String)description
(String)price
(Number)currency
(String)
At the root of the project, create a .env.local
file which uses the MONGODB_URI
environment variable and your MongoDB connection string.
- Copy and paste the following into the
.env.local
file:MONGODB_URI=mongodb://localhost:27017/fish-shop
.
Switch to pages/api/products/index.js
:
-
Delete the import of
lib/products
. -
Import
dbConnect
from thedb/connect.js
file. -
Import the
Product
model. -
Make the
handler
functionasync
andawait
the connection to the database. -
If the
request.method
isGET
,- use the
Product
model to find all products and - return them as a response.
- use the
Switch to pages/index.js
and adapt the frontend:
- replace all instances of
product.id
withproduct._id
.
Check that it works:
- Reload
localhost:3000
; you should still see all fishes.
Switch to pages/api/products/[id].js
and adapt it as explained above:
- To find a single product by its id, you can use the
Product
model and the.findById()
method:Product.findById(id)
. - Delete
lib/products.js
because it is not used anymore.
Open the browser and check the details pages: they should now work as well!
Some of the products already have reviews which are stored in a second collection. Your task is to read from that collection and display the reviews on the right details page.
Open MongoDB Compass and adapt your fish-shop
database:
- Add a new collection called
reviews
; insert the data frombonus-reviews.json
. - Drop the
products
collection; recreate it with the same name, but now insert data from thebonus-products.json
file.- Note: The data in
bonus-products.json
contain areviews
array withids
as a reference to the corresponding review in thereview
collection.
- Note: The data in
Create a schema for the Review
model in the db/models
folder.
The schema should have the following fields:
title
(String)text
(String)rating
(Number)
Update the Product
schema to include a reference to the Review
model:
- Import the
Review
model withimport "./Review";
- Below the
currency
key, add a new line for the reviews; you want to define that it is an array of Object-Ids and has a reference to theReview
schema like so:reviews: { type: [Schema.Types.ObjectId], ref: "Review" },
Switch to pages/api/products/[id].js
and use the .populate
method to integrate the reviews for each product:
const product = await Product.findById(id).populate("reviews");
Finally, update the frontend to display the reviews:
- Switch to
components/Product/index.js
. - Inside of the return statement, check whether the fetched
data
contain any reviews and if so, display them. - Feel free to decide which part of the review data you want to display.
⬇️ You can download the data and assets for the Fish Shop here.
- Unzip the file to get the
resources
folder. - The files are already in the correct structure for the app.
products.json
contains the data for the first task, andbonus-products.json
andbonus-reviews.json
contain the data for the bonus task.
- Import them into the correct collection of your local MongoDB when you are asked to.
In this challenge, you'll further expand a fish shop. This time, you'll create new data in your local MongoDB using mongoose
and refresh the UI programmatically to display the new product immediately.
💡 You can use this template as a starting point. But if you are far enough along with your own Fish Shop App, please use that instead.
If you have not done so, use MongoDB Compass to create a database:
- the database should be called
fish-shop
, - there should be two collections:
products
andreviews
, - download and extract the resources and
- import the data of the
products.json
file into yourproducts
collection; - import the data of the
reviews.json
file into yourreviews
collection.
Create a .env.local
file based on the .env.local.example
. Be sure to spell the name of the database (fish-shop
) correctly.
Run npm run dev
and open localhost:3000
in your browser.
Have a look around:
- there is an overview page with a form to add a new fish and a list of all products below that form;
- when clicking a product in the list, you'll be redirected to a details page;
- note that submitting the form does not do anything right now.
Your task is to refactor the app so that submitting the form creates a new entry in your local MongoDB and refreshes the page to show all products (including the new one).
Switch to pages/api/products/index.js
and write the code for the request.method
POST
:
- Use a
try...catch
block. - Try to:
- Save the product data submitted by the form - accessible in
request.body
- to a variable calledproductData
. - use
Product.create
with theproductData
to create a new document in our collection. - Wait until the new product was saved.
- Respond with a status
201
and the message{ status: "Product created." }
.
- Save the product data submitted by the form - accessible in
- If an error occurs:
- Log the error to the console.
- Respond with a status
400
and the message{ error: error.message }
.
Submitting the form will not yet work because the form does not know that you've created a POST
route it can use.
Switch to components/ProductForm/index.js
:
- There already is a
handleSubmit
function which creates aproductData
object with all relevant data.
Your task is to fetch your new POST
route and send the data to your database. After that use mutate
from useSWR
to refetch the data from the database.
- call
useSWR
in yourJokeForm
component with the API endpoint and destructure themutate
method. - inside the handleSubmit function:
💡 Hint: have a look at the handout if you get stuck here.
- send a "POST" request with
fetch
using the following options as the second argument
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(???),
}
- use the jokeData from the form input as the body of the request
- await the response of the fetch, if the fetch was successful, call the
mutate
method to trigger a data revalidation of the useSWR hooks
Open localhost:3000/
in your browser, submit a new fish and be happy about your shop being expanded!
⬇️ You can download the data and assets for the Fish Shop here.
- Unzip the file to get the
resources
folder. - The files are already in the correct structure for the app.
products.json
contains the data for the all fish,reviews.json
contain the data for all reviews.
- Import them into the correct collection of your local MongoDB when you are asked to.
In this challenge, you'll further expand a fish shop. This time, you'll update and delete data in your local MongoDB using mongoose
.
💡 You can use this template as a starting point. But if you are far enough along with your own Fish Shop App, please use that instead.
If you have not done so, use MongoDB Compass to create a database:
- the database should be called
fish-shop
, - there should be two collections:
products
andreviews
, - download and extract the resources and
- import the data of the
products.json
file into yourproducts
collection; - import the data of the
reviews.json
file into yourreviews
collection.
Create a .env.local
file based on the .env.local.example
. Be sure to spell the name of the database (fish-shop
) correctly.
Run npm run dev
and open localhost:3000
in your browser.
Have a look around:
- there is an overview page with a form to add a new fish and a list of all products below that form;
- when clicking a product in the list, you'll be redirected to a details page.
Your task is to add the functionality for updating and deleting a product. The buttons to do so should be visible on the Product Details Page and after sending the request, the user is redirected to the Overview Page.
Switch to pages/api/products/[id].js
and write the code for the request.method
PUT
:
- Wait for
Product.findByIdAndUpdate(id, { $set: request.body, })
. - Respond with a status
200
and the message{ status: "Product successfully updated." }
.
For now, the ProductForm
component sends a POST
request to your database. We want to reuse the component for editing products and sending PUT
requests as well.
Switch to components/ProductForm/index.js
.
Lift up all logic regarding the creating of the productData
to the pages/index.js
file.
💡 This includes the destructuring of
const { mutate } = useSWR("/api/products");
, thehandleSubmit
function and the import ofuseSWR
.
After doing so,
- rename the
handleSubmit
function tohandleAddProduct
- in the return statement, pass
handleAddProduct
to theProductForm
component as a prop calledonSubmit
.
Switch back to components/ProductForm/index.js
and
- receive the
onSubmit
prop. - use
onSubmit
instead ofhandleSubmit
in the form
💡 Bonus: Pass another new prop to the
ProductForm
component to set the heading of the form dynamically ("Add a new Fish" is not a proper headline when updating the product, right?).
Switch to components/Product/index.js.js
.
You will need the mutate
method to revalidate the product data after a successful update:
- destructure mutate from the object received from the
useSWR
hook.
Below this code, create a handleEditProduct()
function:
- it receives
event
as parameter, - it stores the submitted data in a variable called
productData
(Hint:new FormData
andObject.fromEntries
as already used) - it starts a "PUT" request with
fetch
(hint: this fetch is similar to the "POST" fetch we perform to create products) - uses
mutate
after a successful fetch to update the product detail page.
We need to update the content of our Product component to display the edit form:
- Create a state called
isEditMode
and initialize it withfalse
. - In the return statement, add a
<button>
withtype="button"
,onClick={() => { setIsEditMode(!isEditMode); }}
,- and a proper text.
- In the return statement, display the
ProductForm
component depending on theisEditMode
state (Hint:isEditMode && ...
). - pass our
handleEditProduct
function to theProductForm
as theonSubmit
prop.
Open localhost:3000/
in your browser, switch to a details page, edit a fish and be happy about your shop being expanded!
Switch to pages/api/products/[id].js
and write the code for the request.method
DELETE
:
- Wait for
Product.findByIdAndDelete(id)
. - Respond with a status
200
and the message{ status: "Product successfully deleted." }
.
Deleting a product should be possible from the details page.
Switch to components/Product/index.js
and implement a delete button:
- In the return statement, add a
<button>
withtype="button"
,onClick={() => handleDeleteProduct(id)}
,- and a proper text.
Write the handleDeleteProduct
function:
- Wait for a
fetch()
with two arguments:- the url
/api/products/${id}
and - an options object
{ method: "DELETE" }
- the url
- Save the result in a variable called
response
. - If the
response
isok
,- wait for
response.json()
and usepush("/")
.
- wait for
- If the
response
is notok
, log theresponse.status
as an error to the console.
Open localhost:3000/
in your browser, switch to a details page, delete a fish and be happy about your shop being expanded!
⬇️ You can download the data and assets for the Fish Shop here.
- Unzip the file to get the
resources
folder. - The files are already in the correct structure for the app.
products.json
contains the data for the all fish,reviews.json
contains the data for all reviews.
- Import them into the correct collection of your local MongoDB when you are asked to.
Select the "Browser" tab to view this project. If this project contains tests, select the "Tests" tab to check your progress.
To run project commands locally, you need to install the dependencies using npm i
first.
You can then use the following commands:
npm run dev
to start the development servernpm run build
to create a production buildnpm run start
to start the production buildnpm run test
to run the tests in watch mode (if available)
💡 This project requires a bundler. You can use
npm run dev
to start the development server. You can then view the project in the browser athttp://localhost:3000
. The Live Preview Extension for Visual Studio Code will not work for this project.