codeburst

Bursts of code to power through your day. Web Development articles, tutorials, and news.

Follow publication

Full-stack adventure: weekly meal prep with a custom Blue Apron recipe API

Robert Mion
codeburst
Published in
15 min readSep 25, 2019

--

A demo of what I built, and you could too!

Technologies I used

Why build this app?

It would solve my problem

Thus, to solve my problem…

How did I do all this? Let’s dive in.

I used one recipe to get started

I created a database and collection

Visit https://cloud.mongodb.com/user#/atlas/register/accountProfile to create an account
Or visit https://cloud.mongodb.com/user?nds=true#/atlas/login if you have one already
Once logged in to MongoDB Atlas, you can create a new project
M0 Sandbox cluster tier is ‘Free forever’. You can have one per project.
I created a new free cluster and called it BlueApron
This button shows three options for connecting to a cluster
MongoDB created a GUI to manage clusters. It’s called Compass.
Download and install Compass. Open it. Then click ‘Copy’.
Compass is smart enough to scan your clipboard and recognize the copied connection string
To create a database, you must also create a collection

Creating my first document

{
"name": string,
"sides": string,
"photo": string,
"main_ingredient": string,
"time": [
"min": integer,
"max": integer
],
"servings": integer,
"ingredients": [
{
"name": string,
"quantity": string
},
...
],
"instructions": [
{
"heading": string,
"steps": string
},
...
]
}

Connecting via the mongo shell and inserting a document

Let’s use the Mongo Shell to programmatically insert our first document
mongo "mongodb+srv://cluster-name.mongodb.net/test" --username yourusername
The command line is now connected and ready for me to interface with my cluster

Adding a document to the collection

How to insert a single document into a collection
Here I verified I’m using the correct database and collection. Then I simulate an ‘insertOne’ call
Our first document added to the first collection in our first MongoDB cluster

Break time: work with wife to pick favorite recipes

Quick aside: recipe photos…where to find?

Blue Apron’s official cookbook
Clicking a thumbnail leads to the full recipe page
Right-clicking the image lets me ‘Copy image address’ and use it in my document
https://media.blueapron.com/recipes/22413/square_newsletter_images/1566315246-34-0087-2844/0923_W5_Tuscan-Pork_6138_SQ_Web_hi_res.jpg

Back to work: preparing to build the app

Determining our technology stack

Finally, let’s build the app

App build part 1: use node and express to create a server

$ cd ~/Downloads/
$ mkdir meal-prep-app
$ cd meal-prep-app
$ npm init
$ npm install express
$ touch server.js
const express = require('express');const PORT = process.env.PORT || 5000const app = express();app.listen(PORT, () => console.log(`Example app listening on port ${PORT}!`));
$ node server.js
or
$ npm start
...
Example app listening on port 5000

App build part 2: use express to create an API and routes

...previous linesapp.get('/api/meals', (req, res) => {
// TODO: query database for all documents in 'recipes' collection
}
app.get('/api/menu/', (req, res) => {
// TODO: query database twice
// Once for the most recent document added to a collection
// that stores weekly menus
// Then again for all documents in 'recipes' collection
// that intersect with IDs in the document returned earlier
}
app.get('/api/steps/:id', (req, res) => {
// TODO: query database for 'steps' array
// of document in 'recipes' collection that matches an ID
}
app.get('/api/instructions/:id', (req, res) => {
// TODO: query database for 'instructions' array
// of document in 'recipes' collection that matches an ID
}
app.post('/api/menu', (req, res) => {
// TODO: insert document in 'menus' collection
// that will include an array of recipe document IDs
}

Add build part 3: use mongo to return data

$ npm install mongodb
const MongoClient = require('mongodb').MongoClient;const uri = "mongodb+srv://<username>:<password>@cluster.mongodb.net/database-name?retryWrites=true&w=majority";const client = new MongoClient(uri, { useNewUrlParser: true, useUnifiedTopology: true });
app.get('/api/meals', (req, res) => {
client.connect(err => {
if (err) console.log(err);
const collection = client.db("meals").collection("recipes");
collection.find({}).toArray((err, docs) => res.jsonp(docs));
client.close();
});
}

App build part 4: use Postman to test our API

$ npm start
or
$ node server.js
localhost:5000/api/meals
You should see a single object in the returned array

App build part 5: use plain HTML to create four views

meal-prep-app/
--server.js
--public/
----index.html
----cookbook.html
----steps.html
----ingredients.html
----styles.css
----meals.js
----steps.js
----ingredients.js
----menu.js

App build part 6: use Vue to construct our UI and call our API

<div id="app" class="section">
<div class="container block">
<h2 class="title is-3">Cookbook</h2>
<div class="buttons">
<meal-filter
@change-selected-ingredient="changeSelectedIngredient"
v-for="ingredient in mainIngredients"
:ingredient="ingredient"
:key="ingredient"
:class="{ 'is-active': ingredient === selectedIngredient, 'is-primary': ingredient === selectedIngredient }"
class="button" />
</div>
<div v-if="meals" class="container">
<meal-item
v-for="meal in filteredMeals"
:meal="meal"
:key="meal._id"
@add-to-menu="addToMenu" />
</div>
</div>
</div>
Vue.component('meal-filter', {
props: ['ingredient'],
template: `
<button
@click="$emit('change-selected-ingredient', ingredient)">
{{ ingredient }}
</button>
`,
})
<div class="buttons">
<meal-filter
@change-selected-ingredient="changeSelectedIngredient" />
</div>
new Vue({
el: "#app",
data: {
selectedIngredient: null,
},
methods: {
changeSelectedIngredient(ingredient) {
this.selectedIngredient = ingredient;
this.filterByMainIngredient()
},
filterByMainIngredient() {
this.filteredMeals = this.meals.filter(meal => meal.main_ingredient === this.selectedIngredient)
}
}
})
new Vue({
el: "#app",
data: {
meals: null,
filteredMeals: null,
},
mounted() {
this.fetchMeals()
},
methods: {
fetchMeals() {
fetch('/api/meals')
.then(response => response.json())
.then(meals => {
vm.meals = meals;
vm.filteredMeals = meals;
})
}
})

App build part 7: use Bulma to style our views

App build part 8: use git to version control our app

Log into Github and click to create a New repository
After entering all the pertinent information, be sure to add a .gitignore file for Node apps
Once created, you can clone your repo to your computer using the web URL provided
$ git clone <copied url>

App build part 9: use heroku to deploy our app

From your dashboard, select New > Create new app
Choose a unique name and click ‘Create app’
Refer to the instructions on Heroku’s ‘Deploy’ tab
$ heroku login
...follow prompts
...assuming you're still in meal-prep-app directory...
$ git add .
$ git commit -m "Publishing so wife can test"
$ git push
$ git push heroku master
Choose deployment method 2: GitHub, and connect your repo to enable automatic deploys

App build part 10: use food to celebrate

--

--

Published in codeburst

Bursts of code to power through your day. Web Development articles, tutorials, and news.

No responses yet