All About APIs feat. Express & MongoDB
7:05pm PST: SETUP HELP
7:30pm PST: WORKSHOP STARTS
Disclaimer:
The following workshop will be recorded and shared. We will not be recording anyone’s video except the presenters’ screens, but sound (any questions you ask or answer) will be a part of the recording.
Disclaimer:
This workshop will cover a lot of material relatively quickly. If you are behind at any point, ask for help in the Slack channel (#all-about-apis) or reference the Workshop Companion Guide.
Workshop Handbook & Resources
Workshop Info: See Page
Pre-workshop: See Page
Workshop Companion Notes: See Page
nwPlus Workshop Slack: Join Slack
Timeline (~2 hours)
Introduction
hello world! 👋
Ben Cheung
Workshop TAs
Daniel Ryu
HackCamp Logistics, He/Him
3rd Year Business & CS
Jill Bao
Sponsorship, She/Her
3rd Year Business & CS
Jenny Pan
Internal Dev, She/Her
4th Year Business & CS
Workshop TAs
Allison Chiang
Co-president, She/Her
4th Year CS
Anne Guo
Co-president, She/Her
4th Year Business & CS
Objectives
By the end of the workshop, you would be able to:
Our Hackathons
We're the community behind some of the largest hackathons in the Pacific Northwest, empowering thousands of hackers worldwide every year!
HackCamp
nwHacks
cmd-f
Our Hackathons
HackCamp is a weekend-long hackathon bootcamp on Dec 5-6th designed for beginners and tech lovers worldwide, featuring 9+ hours of workshops and our annual 12-hour-long beginner-friendly hackathon - the largest in North America!
Registration is live.�hackcamp.nwplus.io
Prerequisites
Prereqs
Workshop Handbook & Resources
Workshop Info: See Page
Pre-workshop: See Page
Workshop Companion Notes: See Page
nwPlus Workshop Slack: Join Slack
Overview
What is a backend?
Backend Fundamentals
“Data-access” layer
Client vs Server
NodeJS / Express / APIs
NodeJS
Express
APIs (Application Programming Interface)
Components of a Network Request
A request consists of 4 components:
HTTP Methods
HTTP defines a set of request methods to indicate the desired action to be performed for a given resource.
�GET: used to request data from a specified resource.
POST: used to send data to a server to create/update a resource.
PUT: used to send data to a server to create/update a resource.
HEAD: almost identical to GET, but without the response body.
DELETE: deletes the specified resource.
OPTIONS: describes the communication options for the target resource.
*The two most common HTTP methods are: GET and POST.
We will use these methods for building our CRUD (Create, Read, Update, Delete) functionality. So, focus on GET, POST, PUT, DELETE
Database
We will be using MongoDB for our database. Alternative databases would be MySQL, PostgreSQL, SQLite etc.
Why MongoDB?
Mongoose
Setting up the Backend
Let’s get started with the Backend
First steps:
How to run the server:
1. cd backend
2. npm install
3. nodemon server
Quick look at our Express Server
Quick Look at our Express Server
Quick Look at our Express Server
Connecting to MongoDB
Note:
Replace <password> with nwPlus
Replace <dbname> with the name of the database, BackEndWorkshop
If you do not have a MongoDB server setup, you can use our temporary dummy server by copy pasting the following.
Connecting your MongoDB database
mongoose.connect(uri, {
useUnifiedTopology: true,
useNewUrlParser: true,
useCreateIndex: true,
});
const connection = mongoose.connection;
connection.once("open", () => {
console.log("MongoDB database connection established successfully");
});
Connecting your MongoDB database
�const userSchema = new Schema({
username: {
type: String,
required: true,
unique: true,
trim: true,
minlength: 3,
}, //add age here with appropriate datatype
},
{
timestamps: true,
}
);
Defining your User model.
const userSchema = new Schema( {
username: {
type: String,
required: true,
unique: true,
trim: true,
minlength: 3,
},
age: Number,
},
{
timestamps: true,
}
);
Break
10 mins
Workshop Handbook & Resources
Workshop Info: See Page
Pre-workshop: See Page
Workshop Companion Notes: See Page
nwPlus Workshop Slack: Join Slack
Creating Server Endpoints
We now navigate to \nwPlus Backend Workshop\backend\routes\users.js
Steps:
Create Routes for User Object
/** GET ALL (GET REQUEST)
* Access via: http://localhost:5000/users/
* Example route utilizing a get request to get all the users in the database.
* This route handles incoming HTTP get requests from the /users path
* User.find() is a mongoose method that gets all the users from mongoose atlas database.
* For the .then line, after it finds all the users it returns the users in json format that we got from database
* if there's error - return a error 400 with the message */
router.route("/").get((req, res) => {
User.find() //
.then((users) => res.json(users)) //
.catch((err) => res.status(400).json("Error: " + err));
});
// GET ALL ALTERNATIVE; Access: http://localhost:5000/users/all
router.get("/all", (req, res) => {
//endpoint for accessing all users in database
User.find()
.then((users) => res.send(users)) //Note here.
.catch((err) => console.log(err));
});
GET ALL (GET Request Example)
/* GET ONE (GET REQUEST)
* Access via: http://localhost:5000/users/:id
* xample: http://localhost:5000/users/5f4c647904dcad4a242735e8
* A route for getting a single user's information based on the user's MongoDB id.
* The :id is like a variable. This is object id that is created automatically by mongoDB.
*/
// TODO #8 Fill in the missing pieces of code in order to complete the following Route.
router.get("/:id", (req, res) => {
//endpoint for accessing single user by id in database
User.findById(req.params.id) // find it by id
.then((user) => /*Add missing code here*/)
.catch((err) => /*Add missing code here*/);
});
GET ONE (GET Request Exercise)
/* GET ONE (GET REQUEST)
* Access via: http://localhost:5000/users/:id
* xample: http://localhost:5000/users/5f4c647904dcad4a242735e8
* A route for getting a single user's information based on the user's MongoDB id.
* The :id is like a variable. This is object id that is created automatically by mongoDB.
*/
// TODO #8 Fill in the missing pieces of code in order to complete the following Route.
router.get("/:id", (req, res) => {
//endpoint for accessing single user by id in database
User.findById(req.params.id) // find it by id
.then((user) => res.send(user)) //then return as json ; else return error
.catch((err) => res.status(400).json("Error: " + err));
});
GET ONE (GET Request Solution)
/** POST ONE (POST REQUEST) | Access via: http://localhost:5000/users/add
* This route is for adding a user to the database. It requires the user schema in JSON format to be filled in and the request set to POST. */
// TODO #9 Fill in the missing pieces of code in order to complete the following Route.
// Note: The function .save() saves the new user to the database.
router.post("/add", (req, res) => {
const username = req.body.username; //we assign the username to variable, and create new instance of username
const age = req.body.age || 0;
const newUser = new User({
/**
* add missing code here
*/
});
newUser
.save() // save the new user to the database
.then(() => res.json("User added!")) // return prompt that user is added; else return error message
.catch((err) => res.status(400).json("Error: " + err));
});
POST ONE (POST Request Exercise)
/** POST ONE (POST REQUEST) | Access via: http://localhost:5000/users/add
* This route is for adding a user to the database. It requires the user schema in JSON format to be filled in and the request set to POST. */
// TODO #9 Fill in the missing pieces of code in order to complete the following Route.
// Note: The function .save() saves the new user to the database.
router.post("/add", (req, res) => {
const username = req.body.username; //we assign the username to variable, and create new instance of username
const age = req.body.age || 0;
const newUser = new User({
username,
age,
});
newUser
.save() // save the new user to the database
.then(() => res.json("User added!")) // return prompt that user is added; else return error message
.catch((err) => res.status(400).json("Error: " + err));
});
POST ONE (POST Request Solution)
/* DELETE ONE (DELETE REQUEST)
* Access via: http://localhost:5000/users/:id
* Delete a user based on their MongoDB id.*/
// TODO #10 Fill in the missing pieces of code in order to complete the following Route.
// Note: The function User.findByIdAndDelete(req.params.id) finds a specific id from the MongoDB database.
router./*adding missing code here*/("/:id", (req, res) => {
User./*add missing code here*/(req.params.id)
.then(() => /*adding missing code here*/)
.catch((err) => /*adding missing code here*/);
});
DELETE ONE (Delete Req Exercise)
/* DELETE ONE (DELETE REQUEST)
* Access via: http://localhost:5000/users/:id
* Delete a user based on their MongoDB id.*/
// TODO #10 Fill in the missing pieces of code in order to complete the following Route.
// Note: The function User.findByIdAndDelete(req.params.id) finds a specific id from the MongoDB database.
router.delete("/:id", (req, res) => {
User.findByIdAndDelete(req.params.id)
.then(() => res.json(`User with id ${req.params.id} deleted!`))
.catch((err) => res.status(404).json("Error: " + err));
});
DELETE ONE (Delete Solution)
/** UPDATE ONE (PUT REQUEST) | Access via: http://localhost:5000/users/:id
*/
// TODO #11 Fill in the missing pieces of code in order to complete the following Route.
// Note: The function User.findByIdAndUpdate() finds a specific id from the MongoDB database and updates it
// If you set new: true, findOneAndUpdate() will instead give you the object after update was applied.
router./*adding missing code here*/("/:id", (req, res) => {
const body = /*adding missing code here*/;
const user = {
/*adding missing code here*/
};
User./*adding missing code here*/(req.params.id, user, { new: true })
.then((updatedUser) => res.json(updatedUser))
.catch((err) => res.status(400).json("Error: " + err));
});
Update ONE (PUT Request)
/** UPDATE ONE (PUT REQUEST) | Access via: http://localhost:5000/users/:id
*/
// TODO #11 Fill in the missing pieces of code in order to complete the following Route.
// Note: The function User.findByIdAndUpdate() finds a specific id from the MongoDB database and updates it
// If you set new: true, findOneAndUpdate() will instead give you the object after update was applied.
router.put("/:id", (req, res) => {
const body = req.body;
const user = {
username: body.username,
age: body.age,
};
User.findByIdAndUpdate(req.params.id, user, { new: true })
.then((updatedUser) => res.json(updatedUser))
.catch((err) => res.status(400).json("Error: " + err));
});
Note: TODO #12 is optional and a recommended exercise when you have time. Create another route that updates an existing user in the database using POST REQUEST.
Update ONE (PUT Request Solution)
API Testing with Insomnia
Workshop Handbook & Resources
Workshop Info: See Page
Pre-workshop: See Page
Workshop Companion Notes: See Page
nwPlus Workshop Slack: Join Slack
We now navigate to \nwPlus Backend Workshop\backend\ in our terminal. Run the server.
Once we see our success message, then we can start testing. For any conflicting routes, comment them out.
We can use test using a REST client like Insomnia or Postman.
For the purposes of this workshop, we will be using Insomnia.
{
"username": "Test User 1003",
"age":4
}
Testing our Routes with Insomnia
If a success response is obtained, You have added the user via a POST Request through the POST One Route.
If an error is returned, you should be able to view what type of error it is.
Testing our Routes with Insomnia
Check if your user was added by using the GET ALL request.
Steps:
Next test your routes for
Note: For /:id
* Access via: http://localhost:5000/users/:id
* Example: http://localhost:5000/users/5f4c647904dcad4a242735e8
Check that your user was added.
Resources & Conclusion
Resources for additional learning:
Feel free to reach out and connect.
Additional Resources
Please fill out the feedback form!