Preheat your oven to 400°F and lightly oil a 9x13 inch baking dish. While the oven heats, finely mince the onion into 1/8-inch pieces—this small size helps the onion blend seamlessly into the meatballs rather than creating noticeable chunks. Have all your ingredients measured and ready before you start mixing the meatballs, as this makes the assembly process smooth and prevents the meat from getting overworked.
In a medium bowl, combine the ground beef, bread crumbs, milk, salt, Worcestershire sauce, pepper, minced onion, parsley, egg, and garlic powder. Mix gently with your hands until just combined—overworking the mixture makes the meatballs dense and tough. Shape into 1-inch balls (you should get about 20-24 meatballs) and place them directly in the oiled baking dish. Bake at 400°F for 18-20 minutes until cooked through. Once done, remove the meatballs to a plate and reduce the oven temperature to 350°F.
While the meatballs finish baking, bring a pot of salted water to a boil and cook the spaghetti until al dente (about 1-2 minutes under the package's recommended time). Since the pasta will continue cooking in the oven during the final bake, cooking it slightly underdone ensures the finished casserole won't have mushy noodles. Drain the pasta and toss it lightly with a little olive oil to prevent sticking.
In a small bowl, whisk together the second egg, parsley, 3/4 cup mozzarella cheese, 1/2 cup parmesan cheese, and pepper. This mixture acts as a binder that coats the pasta and adds creamy, cheesy richness throughout the casserole. I find freshly grated parmesan melts much more smoothly than pre-shredded varieties, which contain anti-caking agents that can affect texture.
Add the cheese and egg mixture from Step 4 to the cooked pasta and toss until every strand is evenly coated. Spread this mixture into the same baking dish you used for the meatballs (no need to wash it—the browned bits add flavor). Distribute the cooked meatballs from Step 2 evenly over the pasta, then pour the pasta sauce over everything. Stir gently to combine without breaking apart the meatballs. Cover the dish tightly with foil.
Bake the covered casserole at 350°F for 35 minutes, allowing the flavors to meld and the pasta to finish cooking evenly. Remove the foil carefully to avoid steam burns, then sprinkle the remaining 1.75 cups of shredded mozzarella and 1/2 cup of parmesan cheese over the top. Return to the oven uncovered for 10 minutes until the cheese is melted, bubbly, and lightly golden at the edges. For the best stretch and mozzarella quality, I always shred it from a block rather than using pre-shredded—it has better moisture and melts into silky strands.
Let the casserole rest for 5 minutes before serving—this allows the cheese to set slightly and makes it easier to portion without everything sliding around on the plate. Serve hot with extra parmesan on the side if desired.