Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
105 commits
Select commit Hold shift + click to select a range
9c23fe7
add screenshot
burrowdown May 4, 2024
cd874ff
update gitignore and glossary
burrowdown May 6, 2024
ffe0123
add helpful links section to README
burrowdown May 11, 2024
12f7d5c
add empty html folder
burrowdown May 11, 2024
fd0af6a
add vsocde settings to gitignore
burrowdown May 12, 2024
37d062a
add HTML notes to glossary
burrowdown May 13, 2024
7541959
html first lesson notes
burrowdown May 13, 2024
1f3ec7f
add css intro notes
burrowdown May 13, 2024
5f2017f
add first assignment
burrowdown May 13, 2024
9852019
update installation instructions for windows ssh agent
burrowdown May 17, 2024
d21cdc1
add assignment table to readme
burrowdown May 17, 2024
d4b9d96
add troubleshooting and git steps doc
burrowdown May 21, 2024
cbce88a
add image to git instructions
burrowdown May 22, 2024
5e1f20d
add flexbox lesson
burrowdown May 24, 2024
5356fef
add flexbox assignment
burrowdown May 24, 2024
11d8b87
add flexbox assignment to README
burrowdown May 24, 2024
1e94f12
update html assignment instructions to include PR requirement
burrowdown May 24, 2024
760f5c1
Update README.md
Adesoji1 May 24, 2024
2a891bb
Merge pull request #13 from Adesoji1/main
Adesoji1 May 24, 2024
438089c
add color and size lesson notes
burrowdown May 25, 2024
607e98b
add grid lesson notes
burrowdown May 29, 2024
9c7669a
add lesson notes for combining flexbox and grid
burrowdown May 29, 2024
e4082b5
add media queries lesson notes
burrowdown May 29, 2024
80fb682
add grid assignment
burrowdown May 29, 2024
4e0a579
add grid assignment to readme
burrowdown May 29, 2024
c020948
remove duplicate CSS from media queries notes
burrowdown May 29, 2024
9eb15eb
fix link in grid assignment
burrowdown May 29, 2024
040c46b
Merge pull request #26 from IBT-learning/css-grid
burrowdown May 29, 2024
beb848e
Responsive Design and Media Queries (#33)
burrowdown May 30, 2024
9550fee
minor updates to assignment instructions
burrowdown Jun 1, 2024
e4af9d2
add html final project description
burrowdown Jun 2, 2024
0026881
fix assignment title
burrowdown Jun 2, 2024
cb01a60
Create gitcheatsheet.md
Adesoji1 Jun 5, 2024
6a58c75
Add files via upload
Adesoji1 Jun 5, 2024
0eb0575
Rename Git/Screenshot from 2024-06-05 16-40-14.png to Gitcommands.png
Adesoji1 Jun 5, 2024
bc9bad4
Delete Gitcommands.png
Adesoji1 Jun 5, 2024
964be98
Add files via upload
Adesoji1 Jun 5, 2024
04aba04
Rename Git/Screenshot from 2024-06-05 16-40-14.png to Gitcomands.png
Adesoji1 Jun 5, 2024
e8835ca
Delete Gitcomands.png
Adesoji1 Jun 5, 2024
30cb9c0
Create text.md
Adesoji1 Jun 5, 2024
bd53480
Add files via upload
Adesoji1 Jun 5, 2024
5c4a001
Delete Git/text.md
Adesoji1 Jun 5, 2024
d10df0e
Update gitcheatsheet.md
Adesoji1 Jun 5, 2024
769abbd
Update README.md
Adesoji1 Jun 5, 2024
f901695
Update README.md
Adesoji1 Jun 5, 2024
17c0d3a
Merge pull request #72 from Adesoji1/main
Adesoji1 Jun 5, 2024
58e0f25
make adjustments to readme
burrowdown Jun 9, 2024
7070fcc
add JS string and number lesson notes (#91)
burrowdown Jun 10, 2024
3ba9c87
add JS array notes
burrowdown Jun 16, 2024
207b106
Boolean notes 2 (#133)
burrowdown Jun 23, 2024
e700ad6
add function lesson notes
burrowdown Jun 29, 2024
c28a1fa
add functions assignment
burrowdown Jul 4, 2024
e64c818
readme typo
burrowdown Jul 4, 2024
2e47287
add lesson notes and assignment (#195)
burrowdown Jul 6, 2024
aba4fe1
add class lecture notes and assignment
burrowdown Jul 11, 2024
d88c619
add callbacks session notes and assignment
burrowdown Jul 14, 2024
9e65546
Js final (#230)
burrowdown Jul 17, 2024
0280fd0
add dom and event listener lesson notes
burrowdown Jul 25, 2024
fe8a306
Merge remote-tracking branch 'origin'
burrowdown Jul 25, 2024
2b51d9e
Poll (#276)
burrowdown Jul 26, 2024
a04f94d
Update README.md (#277)
burrowdown Jul 26, 2024
d45454e
Fetch (#320)
burrowdown Aug 11, 2024
0e12979
add assignment
burrowdown Aug 11, 2024
ffee1f1
update assignments
burrowdown Aug 11, 2024
65cf983
update web final project assignment
burrowdown Aug 12, 2024
6f933df
Npm (#340)
burrowdown Aug 18, 2024
12ccd98
add type:module to npm notes
burrowdown Aug 18, 2024
1bfc7da
add express intro notes and assignment
burrowdown Aug 19, 2024
30f97a6
add crud server lesson notes, and server assignment 3
burrowdown Aug 25, 2024
c286612
fix typos
burrowdown Aug 29, 2024
de6deae
Middleware (#391)
burrowdown Sep 2, 2024
24c9b20
Cookies (#406)
burrowdown Sep 8, 2024
92177bc
add mongodb installation instructions
burrowdown Sep 11, 2024
8f8e600
add mongo success image
burrowdown Sep 11, 2024
182fc48
add db notes
burrowdown Sep 17, 2024
963fff4
add mongo driver lesson setup
burrowdown Sep 17, 2024
ae81ae4
add recipes.json
burrowdown Sep 17, 2024
c1238ea
add assignment 0
burrowdown Sep 17, 2024
ba5b534
add assignment to readme
burrowdown Sep 17, 2024
a6cb92e
Db intro (#428)
burrowdown Sep 17, 2024
16665de
add lesson notes and assignment
burrowdown Sep 18, 2024
759bcaf
merge conflic
burrowdown Sep 18, 2024
b343fb3
fix assignment title and package lock for mongo driver lesson
burrowdown Sep 18, 2024
6f9a3a1
fix assignment title
burrowdown Sep 18, 2024
d24c53c
Mongoose (#442)
burrowdown Sep 25, 2024
78e2e70
Auth (#459)
burrowdown Oct 1, 2024
e3d0caa
Dotenv (#483)
burrowdown Oct 10, 2024
402a283
add react intro lesson notes
burrowdown Oct 14, 2024
1ee891a
React state (#515)
burrowdown Oct 24, 2024
58f4e45
Lifting state (#516)
burrowdown Oct 25, 2024
e82d221
add examples of conditional rendering to lifting-state lesson notes
burrowdown Oct 26, 2024
383357c
Useeffect (#518)
burrowdown Oct 29, 2024
dbc7c9d
Remote fetch (#519)
burrowdown Oct 31, 2024
2da056d
add .vite to gitignore
burrowdown Nov 2, 2024
2ca0c32
Router (#533)
burrowdown Nov 8, 2024
381afea
typo
burrowdown Nov 8, 2024
d5fc4eb
Testing (#554)
burrowdown Nov 20, 2024
5da2d5e
typos
burrowdown Nov 20, 2024
98482f5
Router auth (#541)
burrowdown Nov 20, 2024
484ef7f
Client tests (#559)
burrowdown Nov 27, 2024
7f0f34e
fix assignment errors
burrowdown Nov 27, 2024
2530efb
Correct file name in web-api/index.html
queenhabeebah Jan 11, 2025
e2f02f9
Merge
queenhabeebah Jan 13, 2025
d4c98ca
Merge successfully
queenhabeebah Jan 13, 2025
b9774c3
Add a recipe to the shared recipe
queenhabeebah Jan 13, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
.env

notes/
notes.*

.vscode/


node_modules/
51 changes: 51 additions & 0 deletions 5-database/assignments/db-assignment-2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Database Assignment #2: Make a CRUD app with Mongoose

For this assignment, you'll set up a simple CRUD app with five endpoints for our Recipe app.

### Create a local database for your Recipe data

You'll want to start with some starter data, so before you begin...

1. pull the most recent `main` into your personal branch. You can find instructions for that on [server assignment #1](../../4-server/assignments/server-assignment-1.md)
1. open Compass, and use the `+` symbol to "add database". You can name your database and the collection both "recipes"
1. click the green "Add Data" button, and select "Import JSON or CSV file"
1. Navigate to the `recipes.json` file that is in this repository, and upload it
1. You should now see several documents with recipes!

### Set up an express server

This will be almost identical to your last assignment, but you'll install `mongoose` instead of `mongodb`

### Create a Recipe Schema

Create a Schema with the following fields:

- title: string
- author: string
- instructions: string
- ingredients: array of strings
- to indicate an array of strings use `ingredients: [String]` or `ingredients: [{type: String, maxlength: ...}]`

What other requirements do you think should be set? What are reasonable `maxlength` values? Are they required? Should they have default values?

### Connect to the mongodb server

Now that your Schema is set, you can import it into your controller and start making queries. But first, you must connect to the database server.

Follow how it's done in the `mongoose-schemas` lesson. Remember that the connection string must end with the name of _your_ local database, not mine and not the one from the example.

Import the Recipe schema into your controller, and set up a "get all" endpoint with `await Recipe.find()`. You should now see all of your local recipe documents in Postman when you hit that endpoint.

### Create your CRUD endpoints

Set up and implement five endpoints:

- Get all (return all documents)
- Get one by ID (return a single document)
- Create a new document (add a new document to the database)
- Update an existing document by ID
- Delete an existing document by ID

### Extra challenge (optional)

Make sure you're handling all possible user errors, and sending specific messages back that would help the user understand the problem.
37 changes: 37 additions & 0 deletions 5-database/assignments/db-assignment-3.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Database Assignment #3: Add users to your recipe app

You have already written a CRUD app for recipes. Now let's add user authentication, so that anybody can read recipes but only certain users can add, update, or delete recipes. The goal is that when registered users create a recipe, they will "own" that recipe, so that only they are allowed to modify or delete it.

### Add users to your app

Add a `User` model and a `users.js` controller with endpoints for creating an account, and for logging in. (So far this will look very similar to what's in the [user-auth lesson notes](../user-auth/), and that's okay!) It's up to you whether you want to define any additional fields on the user schema, beyond `email` and `password`.

### Add token validation

Create a middleware that validates tokens, (this will also look very similar to the lesson,) and then apply that middleware _only_ to your Create, Update, and Delete endpoints (that part will be somewhat different from the lesson example!)

Make sure your validation is fully working, and only valid users can access the restricted endpoints, before you proceed to the next step.

### Now for the interesting part...

If only users who created a recipe can make changes, that means our Schema needs to be updated to start recording who created each recipe. Each Recipe will have a `createdBy` field that stored an ObjectId, which represents a user in the database. (This is called a "foreign key", where a key from another collection is stored as a field on our documents.)

1. Add a field to the Recipe schema that looks like this:
- `createdBy: mongoose.Schema.ObjectId` (it doesn't need more because we don't need to specify any other constraints on this field)
1. Change your Create endpoint to add the validated user's `_id` to the Recipe object before you save it.
- How do you know what the validated user's `_id` is? Try adding the user object to the `req` during the `tokenValidation` middelware. Remember that middlewares always have access to the request and response objects, and can modify them! Those modifications will then be received by the next function in the call stack.
1. Change your Update endpoint to check whether the validated user matches the `createdBy` field on the recipe.
- Remember to include sensible error handling. What do you think would be the right HTTP status code for a request on a recipe the user doesn't own?
1. Change your Delete endpoint to do the same thing!

> NOTE: You will need multiple users in your database to thoroughly test these endpoints

### Extra challenges

##### Cookies

In the next lesson I will demonstrate how to add the user's JWT to a browser cookie, but you already know everything you need to figure that out on your own! Don't worry too much about testing it, but if you like you could just open up any of your web-api assignments and have them manually make a fetch with your credentials, then check to see if your JWT is in Storage > Cookies

##### Get all by user

Try adding a new GET endpoint that returns all of the recipes that were created by a certain user. You will need to use `.find()` with a query object, similar to the query objects we use for `.findOne()`
85 changes: 85 additions & 0 deletions 5-database/assignments/db-final-project.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# Database Final Project: Blog database

Your blog currently has a server with one or more endpoints for serving data to the front-end. But right now, the data is just stored in a placeholder file. We want to set up a database to create and store actual data!

We will want a database (I suggest "BlogAPI" unless your blog has a name) with two collections, for "posts" and "users". You should already have placeholder data from earlier phases of this project, now is the time to upload that through Compass as your "posts". You don't need to create the users collection manually, it will be created automatically by our schema constructor.

## Set up your database

Install `mongoose` and `dotenv` in your project. Write a `dbConnect` function that creates the mongodb connection via mongoose, and call that function in your `app.js`.

Use dotenv! Remember the two kinds of things that must be stored in environment variables are:

- secrets (such as JWT key and SALT)
- anything related to the development environment, such as port numbers, and paths outside the package. The path to your database localhost will be one of these. Remember to include the name of your database at the end of the connection string

## Add user authentication

Even though you probably don't have a web client that includes the register and login actions, you will need those endpoints in order to create valid test users and generate tokens. We will worry about adding that functionality to the client in the next unit when we learn React!

User authentication in MongoDB is fairly straightforward, so this can be very close to what you did for [users in the recipe app](./db-assignment-3.md). You will want to put more thought into the users schema for this one, though. What will "users" on your blog have? Take a look at how you designed your blog's user profile page, what is there? A bio, a tagline, a profile photo? Do they have a handle or display name that is different from their username? Make sure everything is represented on the user schema.

Remember, not every field has to be required! In fact, only fields that a user _must_ fill out when they sign up for an account should be required.

> Note: For now, if you have photos you can serve them as static files, like we learned in the Server unit. Have your documents store the _static path_ of the image. We will learn how to handle things like user-uploaded images in a later unit.

## Create your Posts schema

Your posts need to include at least these required fields:

- body: String (this is the text of the post)
- userId: ObjectId (the user who made the post)
- createdAt: timestamp, we don't set these fields manually. See the [schemas lesson notes](../mongoose-schemas/models/Book.js) for an example

What else? Again, look at your site design to see what else is included. Do posts have titles? Images? (Any information related to the user, such as their display name, can be accessed through the userId and doesn't need to be included on this schema separately.) Take the time to think through which fields are required and which are optional.

Once your schema is created, go back to your endpoints and update them to use the schema.

Now is a good time to think about which of your endpoints need to be behind token validation. Often the GET endpoints will be public and the POST, PUT, and DELETE endpoints will require authentication. But this is your blog site and your decision!

## Test with your client!

If you have updated your endpoints without changing their functionality, you shouldn't have to make any changes to calls to your public endpoints, and your blog site should still work as it previously does.

For any endpoints you put behind token validation, you will need to add something to your `fetch` calls. They will need a second argument that looks like this:

```javascript
fetch(`http://localhost:4000/api/...`, {
headers: {
authorization: token goes here...,
},
})
```

You will just paste in a hard-coded token here for testing purposes, but once we implement real login behavior, the client will be able to get the user's token from the cookies.

#### CORS

Depending on your browser, you may get a "Cross-Origin Resource Sharing" error in your browser. We will talk more about CORS in a later unit, but in the meantime you can probably circumvent the error with these steps

- run `npm i cors` in your npm package
- in your app.js
- `import cors from "cors"` at the top
- `app.use(cors())` somewhere _before_ you apply your controllers

## Thinking forward

Right now your client doesn't do a whole lot, so you might only have a couple of GET endpoints. Soon the app will need to do a lot more! Can you "write ahead" some endpoints that will be ready for the next phase of this project? What are the actions we foresee? Creating, editing, and deleting posts are obvious ones. Can you think of anything else a user might be able to do in your blog app?

What about users? Can they change their bios, display names, or anything else? (Leave profile photos for a future unit, for now let's keep using hard-coded ones, or not include them at all.) Can users delete their whole profiles?

If you're adding CRUD endpoints for users, does it make sense for those to be in the same controller as the authorization-related endpoints, `register` and `login`? Think about separation of concerns. It's okay to add more controllers if it keeps things tidy!

(You may be tempted at this point to start thinking about likes and comments, but unless you are feeling very confident with this material I will discourage you from making the project overly complicated at this phase. Likes and comments will probably require more schemas, and more thought about foreign key relationships.)

I leave it to you how much of this you want to do now, but I will warn you that React is a big topic and the next projects will be big lifts! If you write these endpoints now, later you will be glad you did.

Again, it's not necessary to implement this functionality in your client yet. Just write the endpoints and test them with Postman. Don't forget to include tokens as needed, and don't forget that your tokens expire!

## Refreshers (optional)

Unless you really love writing event listeners, I don't think that actually implementing new functionality (such as creating posts) into your client at this point will be a good use of your time. We will soon be learning a whole new approach to writing web clients.

However, some of the actions we will be implementing soon will require new designs. Take some time to think about all these user actions, and whether they're already reflected in your blog's HTML. Where will users add new posts from? Is there a field at the top of the home page, or is it a separate page? What about login? Registering a new account? Editing or deleting a post?

Now would be a great time to build new pages or adjust your existing pages to prepare for the next phases of the project. Any structural HTML you write will be usable in React, but don't worry about making the forms actually submit or anything like that.
6 changes: 6 additions & 0 deletions 5-database/assignments/recipes.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,11 @@
"author": "Danny Burrow",
"ingredients": ["whole pork shoulder", "bbq spice mix"],
"instructions": "Pat pork shoulder dry and liberally coat with salt and spices. Place on a rack in oven at 225F for 8-12 hours, until roast is fall-apart tender. Cool slightly, then shred. Mix with skimmed and strained pan drippings as desired. "
},
{
"title": "Grilled Chicken",
"author": "Habeebah Aleilo",
"ingredients": ["whole chicken", "spice", "herbs", "oil", "salt"],
"instructions": "Marinate the chicken with a blend of spices, herbs, salt, and oil, let it sit for at least an hour, then grill over medium heat until golden brown and fully cooked, turning occasionally."
}
]
17 changes: 17 additions & 0 deletions 5-database/mongoose-schemas/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import express from "express"
import cors from "cors"
import { dbConnect } from "./db.js"

const app = express()
const PORT = 4000

app.use(express.json())
app.use(cors())

import router from "./controllers/books.js"
app.use("/books", router)

app.listen(PORT, () => {
dbConnect()
console.log(`[server]: listening on port ${PORT}`)
})
75 changes: 75 additions & 0 deletions 5-database/mongoose-schemas/controllers/books.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import express from "express"
const router = express.Router()
import { mongoose } from "../db.js"

import Book from "../models/Book.js"

router.get("/", async (req, res) => {
// the queries work exactly the same as they do on the driver
// so .find() with no arguments returns the entire collection
const books = await Book.find()
res.send(books)
})

router.get("/find/:bookId", async (req, res) => {
if (mongoose.Types.ObjectId.isValid(req.params.bookId)) {
// .findOne() returns a single document, according to the given filter object
const book = await Book.findOne({ _id: req.params.bookId })
book ? res.json(book) : res.status(404).send("book not found")
} else {
res.status(404).send("invalid book id")
}
})

router.post("/", async (req, res) => {
try {
const newBook = new Book(req.body)
// when adding new documents, .save() is a separate operation
// in case we want to make any changes to the document
// in between creating it and sending it to the database
await newBook.save()
res.send(`added ${req.body.title} by ${req.body.author}`)
} catch (err) {
res.status(422).send(err.errors.title)
}
})

router.put("/update/:bookId", async (req, res) => {
if (mongoose.Types.ObjectId.isValid(req.params.bookId)) {
const filter = { _id: req.params.bookId }
const body = req.body
const options = { upsert: true }
// upsert means "update or insert"
// it will create a new document if it can't find any that match the filter
// it is "false" by default, so we only need to set this option
// if we want it to be true

const book = await Book.updateOne(filter, body, options)
console.log(book)
if (book.modifiedCount === 1) {
res.send("book successfully updated")
} else if (book.upsertedCount === 1) {
res.send("book successfully added")
} else {
res.send("operation failed")
}
} else {
res.status(404).send("invalid book id")
}
})

router.delete("/delete/:bookId", async (req, res) => {
if (mongoose.Types.ObjectId.isValid(req.params.bookId)) {
const filter = { _id: req.params.bookId }
const book = await Book.deleteOne(filter)
if (book.deletedCount === 1) {
res.send("successfully deleted")
} else {
res.send("something went wrong")
}
} else {
res.status(404).send("invalid book id")
}
})

export default router
12 changes: 12 additions & 0 deletions 5-database/mongoose-schemas/db.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import mongoose from "mongoose"

const dbConnect = async () => {
try {
await mongoose.connect("mongodb://localhost:27017/BooksAPI")
console.log(`[database]: connected to db`)
} catch (err) {
console.warn(`[database error]: ${err}`)
}
}

export { dbConnect, mongoose }
27 changes: 27 additions & 0 deletions 5-database/mongoose-schemas/models/Book.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { mongoose } from "../db.js"

const Book = new mongoose.Schema(
{
title: {
type: String,
required: true,
maxlength: 150,
trim: true,
},
author: {
type: String,
required: false,
default: "Author Unknown",
maxlength: 150,
},
ratings: [{ type: String, maxlength: 50 }],
// genre: {
// type: String,
// required: false,
// enum: ["nonfiction", "history", "whatever"],
// },
},
{ timestamps: true }
)

export default mongoose.model("book", Book)
Loading