Express JS - adding a route within a server to handle POST requests

I’m new to Javascript and I’m trying to learn express and create an application that will allow users to create new recipes, browse existing recipes, and view recipes.

I’ve got my server running by typing recipeserver.js in the cmd bar and then typing localhost:3000 in my address bar on google chrome. So far it loads the index.html homepage and from there, I am able to click on a link titled “Create a Recipe” which leads me to the create.html page that looks like this:

MyPage

Initially, there will be only three recipes on the server, which are included in the database object within the recipeserver.js code I’ve included below. The create.html page allows a user to enter recipe information. When the Save Recipe button is clicked, the addrecipe.js file is supposed to send the recipe data to the server using a POST request to the resource /recipes

Within the server code, all recipes will be stored in a single object called database. The keys of this object will be unique IDs and the values will be the recipes associated with those IDs. I’m stuck on a task where I’m supposed to add a route within the server code to handle POST requests to the /recipes resource. The handler for this route should:

  1. Extract the recipe object included in the POST request body
  2. Generate a unique ID for the new recipe (Etc. a basic integer that increases every time a recipe is added.)
  3. Add a new entry into the recipes object with the key being the unique ID and the value being the recipe object.

When testing my code by adding a few recipes to my server, I should be able to just log the contents of the recipes object to see that it is storing the correct data, like in the picture below (this picture isn’t mine):

example

So as shown in the first picture of my screen, I filled in the contents of the recipe I want to add in create.html. When I click on the “Save Recipe” button however, instead of loading the contents of the recipe into my cmd window, I get the error:

TypeError: C:\Downloads\recipeApplication\views\recipes.pug:8
	6|     div#main
	7|         h1 List of Recipes:
  > 8|         each recipe in recipes
	9|             a(href="/recipes/" + recipe.id) #{recipe.name}
	10|             br
	11|

Cannot read property 'length' of undefined

I’m a little stumped on how to a route within the server code to handle POST requests to the /recipes resource. I made a function called loadRecipes() that I’m trying to do all this in. I attempted to create a new id and increment it by 1 like the task suggests. I’m having trouble extracting the recipe object included in the POST request body. I attempted this and ended up commenting it out as it created the same error. I’m just trying to get the Save Recipe button to work so that the recipe that is added prints its contents in the cmd bar like in the 2nd picture, but I’m really lost and overwhelmed with the amount of information that comes up when I try to search for a solution and would appreciate some help in getting this to work.

Here’s all my code incase anyone wants to run it but I believe my problem just lies in the recipeserver.js file. When the Save Recipe button is clicked, the addrecipe.js file sends the recipe data to the server using a POST request to the resource /recipes.

recipeserver.js:

const express = require('express');
const fs = require("fs");
const shortId = require("short-id");
const session = require('express-session');
const app = express();
const pug = require("pug");
const port = 3000;


let database = {
	"0":{
		"ingredients":
		[
			{"name":"Crab","unit":"Tsp","amount":3},
			{"name":"Peas","unit":"Cup","amount":12},
			{"name":"Basil","unit":"Tbsp","amount":10},
			{"name":"Cumin","unit":"Liter","amount":3},
			{"name":"Salt","unit":"Tbsp","amount":1}
		],

		"name":"Boiled Crab with Peas",
		"preptime":"13",
		"cooktime":"78",
		"description":"A boring recipe using Crab and Peas",
		"id":"0"
	},
	"1":{
		"ingredients":
		[
			{"name":"Peanuts","unit":"Liter","amount":10},
			{"name":"Artichoke","unit":"Tsp","amount":3},
			{"name":"Basil","unit":"Cup","amount":11},
			{"name":"Sage","unit":"Grams","amount":13},
			{"name":"Pepper","unit":"Cup","amount":1}
		],

		"name":"Boiled Peanuts with Artichoke",
		"preptime":"73",
		"cooktime":"74",
		"description":"A exciting recipe using Peanuts and Artichoke",
		"id":"1"
	},
	"2":{
		"ingredients":
		[
			{"name":"Lobster","unit":"Tsp","amount":14},
			{"name":"Brussel Sprouts","unit":"Liter","amount":14},
			{"name":"Sage","unit":"Tbsp","amount":3},
			{"name":"Thyme","unit":"Tbsp","amount":12},
			{"name":"Pepper","unit":"Tsp","amount":10},
			{"name":"Cumin","unit":"Tbsp","amount":11}
		],

		"name":"Spicy Lobster with Brussel Sprouts",
		"preptime":"86",
		"cooktime":"19",
		"description":"A tasty recipe using Lobster and Brussel Sprouts",
		"id":"2"
	}
}


let recipes = {};

for (let recipe in database) {
  recipes[recipe.id] = recipe;
};

app.set("view engine", "pug");
app.use(express.static("public"));
app.use(express.json());
app.use(express.urlencoded({extended: true}));



app.get("/addrecipe.js", getAddRecipeJS);
app.get("/recipes", loadRecipes);
app.get("/recipe", loadRecipe);
app.route("/recipes", loadRecipes);
app.post("/recipes", loadRecipes);

let id = 1;	
function loadRecipes(request, response, next){
	response.status(200).render("recipes.pug", {"session": request.session});
	/*
	console.log("Request received!", request.body);
	const newId = shortId.generate();
	recipes[newId] = {
	  id: newId,
	  type: "recipe",
	  ...request.body,
	};
	response.sendStatus(201);
	*/
	id++;
}

function loadRecipe(req, res, next){
	res.status(200).render("recipe.pug", {"session": req.session});
}

function getAddRecipeJS(req, res, next){
	fs.readFile("addrecipe.js", function(err, data){
		if(err){
			res.statusCode = 500;
			res.end("Error reading file.");
			return;
		}
		res.status(200).send(data);
		return;
	});
}

app.listen(port);
console.log(`Server listening at http://localhost:${port}`);

index.html:

<html>

	<head><title>Recipe App Home Page</title></head>
	
	<body>
		<h1>Welcome to the Recipe App</h1>
		<br>
		<a href="/create.html">Create a Recipe</a><br>
		<a href="/recipes">Browse Recipes</a><br>		
	</body>
</html>

create.html:

<html>
	<head><title>Create a Recipe</title></head>
	
	<body>
		<script src="/js/addrecipe.js"></script>
		
		<button type="button" onclick="genRandom()">Generate Random Recipe Data</button>
		<button type="button" onclick="submit()">Save Recipe</button>
		<br><br>
		
		Recipe Name: <input type="textbox" id="recipename" size="50"><br>
		
		Prep Time: <input type="textbox" id="preptime" size="50"><br>
		
		Cook Time: <input type="textbox" id="cooktime" size="50"><br>
		
		Description: <textarea rows="5" cols="50" id="description"></textarea><br><br>
		
		Add ingredients:<br>
		Unit: <select id="unit">
		<option value="Tsp">Teaspoon</option>
		<option value="Tbsp">Tbsp</option>
		<option value="Cup">Cup</option>
		<option value="Liter">Liter</option>
		<option value="Gram">Gram</option>
		</select><br>
		
		Amount: <input type="textbox" id="amount"><br>
		
		Ingredient: <input type="textbox" id="ingredient"><br>
		
		<button type="button" id="add" onclick="addIngredient()">Add Ingredient</button>
		<br><br>
		<div id="ingredients">
		
		</div><br>
		<button type="button" id="submit" onclick="submit()">Save Recipe</button>
	</body>
</html>

addrecipe.js:

let descriptors = ["Sweet", "Spicy", "BBQ", "Braised", "Deconstructed", "Broiled", "Boiled", "Flambeed", "Raw", "Smoked", "Butterflied", "Cured", "Grilled", "Poached"];
let proteins = ["Chicken", "Beef", "Lobster", "Shrimp", "Crab", "Turkey", "Duck", "Tofu", "Chickpeas", "Lentils", "Peanuts", "Kangaroo", "Human", "Goose", "Fish", "Pork", "Eggs", "Deer"];
let accompany = ["Broccoli", "Carrots", "Peas", "Potato", "Kale", "Banana", "Artichoke", "Asparagus", "Beans", "Broccoli", "Brussel Sprouts", "Celery", "Melon", "Mushrooms", "Pumpkin"];
let spices = ["Salt", "Pepper", "Basil", "Thyme", "Sage", "Cumin"];
let mealDescriptors = ["tasty", "mediocre", "very good", "boring", "exciting", "delicious", "easy", "ridiculously complex"];
let units = ["Tbsp", "Tsp", "Cup", "Liter", "Grams"]

let recipe = {ingredients: []};

function addIngredient(){
	let name = document.getElementById("ingredient").value;
	let amount = document.getElementById("amount").value;
	let unit = document.getElementById("unit").value;
	let ingredient = {name, amount, unit};
	recipe.ingredients.push(ingredient);
	updateIngredients();
}

function updateIngredients(){
	let innerHTML = "";
	recipe.ingredients.forEach(ingredient => {
		innerHTML += ingredient.amount + " " + ingredient.unit + " " + ingredient.name + "<br>";
	});
	document.getElementById("ingredients").innerHTML = innerHTML;
}

function submit(){
	recipe.name = document.getElementById("recipename").value;
	recipe.preptime = document.getElementById("preptime").value;
	recipe.cooktime = document.getElementById("cooktime").value;
	recipe.description = document.getElementById("description").value;
	
	let req = new XMLHttpRequest();
	req.onreadystatechange = function() {
		if(this.readyState==4 && this.status==200){
			alert("recipe saved");
		}
	}
	
	//Send a POST request to the server containing the recipe data
	req.open("POST", `/recipes`);
	req.setRequestHeader("Content-Type", "application/json");
	req.send(JSON.stringify(recipe));
}

recipes.pug:

html
	head
		title Recipes
body
	a(href="/create.html") add a recipe
	div#main
		h1 List of Recipes:
		each recipe in recipes
			a(href="/recipes/" + recipe.id) #{recipe.name}
			br

recipe.pug:

html
	head
		title #{recipe.name}
body

	div#main
		h1 #{recipe.name}
		br

Where does the request.session comes from?