Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
abedf07
chore: add week 2 legacy material
magdazelena Oct 6, 2025
7a7d46c
Expand on some of the learning goals
adamblanchard Oct 7, 2025
e2a2a25
Removed some old legacy paragraphs
adamblanchard Oct 7, 2025
e33a4f0
Merge branch 'main' into 58-backend-nodejs-week2
adamblanchard Oct 28, 2025
0447024
Tidy up and add postman session plan notes and exercises
adamblanchard Oct 28, 2025
7cd0516
Added weeks and docs to node module for gitbook
adamblanchard Oct 28, 2025
957204b
fixed some gitbook links
adamblanchard Oct 28, 2025
ee2163a
tidied up where postman is mentioned, and added a todo idea for assig…
adamblanchard Oct 28, 2025
826fc83
added phonebook examples and content to teach knex and refresh crud
adamblanchard Nov 4, 2025
8dfeb51
removed old phonebook instructions
adamblanchard Nov 4, 2025
ed49536
Renamed post enpoint exercise to continue the correct sequence
adamblanchard Nov 4, 2025
3133f65
added placeholders for put and delete exercises
adamblanchard Nov 4, 2025
ae233a6
Linked placeholder todos
adamblanchard Nov 4, 2025
e160bbb
Tidied up learning goals and session plans
adamblanchard Nov 4, 2025
db71b66
Fixed linting
adamblanchard Nov 4, 2025
f34e3ae
Added more info to explain knex
adamblanchard Nov 4, 2025
7c0cefe
Apply suggestion from @magdazelena
adamblanchard Nov 10, 2025
aeaa73a
Apply suggestions from code review
adamblanchard Nov 10, 2025
db336e0
Remove meal sharing tasks and move knex cheatsheet to prep
adamblanchard Nov 10, 2025
776f142
simplify assignment
adamblanchard Nov 10, 2025
bf506e2
remove sqlite todo
adamblanchard Nov 10, 2025
95a0c57
simplifieid live coding instructions
adamblanchard Nov 10, 2025
9acfe5b
removed redundent instruciton
adamblanchard Nov 10, 2025
00b22d5
expand on the assignment to mirror more of the sessions learning goals
adamblanchard Nov 10, 2025
5b805a1
linting fixes
adamblanchard Nov 10, 2025
42a6daf
updated session plan wording to be a little less mixed between mentor…
adamblanchard Nov 10, 2025
2ba2dfa
added initial content for error handling
adamblanchard Nov 11, 2025
0738758
Added package.json etc. for live coding materials
adamblanchard Nov 11, 2025
b9e63e6
Actually include package.json etc. in this commit
adamblanchard Nov 11, 2025
6bed336
Move rest content from readme to session plan
adamblanchard Nov 11, 2025
f32ad49
linting fixes
adamblanchard Nov 11, 2025
dad8970
fixed header order in readme
adamblanchard Nov 25, 2025
49fae84
Added session materials to gitbook sidebar
adamblanchard Nov 25, 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
19 changes: 19 additions & 0 deletions SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,25 @@
- [Session Plan](courses/backend/databases/week2/session-plan.md)
- [Assignment](courses/backend/databases/week2/assignment.md)
- [Node](courses/backend/node/README.md)
- [Week 1](courses/backend/node/week1/README.md)
- [Session Materials](courses/backend/node/week1/session-materials/)
- [1. Server](courses/backend/node/week1/session-materials/01-server.md)
- [2. Schema](courses/backend/node/week1/session-materials/02-schema.md)
- [3. Routing](courses/backend/node/week1/session-materials/03-routing.md)
- [4. Database connection](courses/backend/node/week1/session-materials/04-database-connection.md)
- [5. GET endpoints](courses/backend/node/week1/session-materials/05-get-endpoints.md)
- [6. Auth](courses/backend/node/week1/session-materials/06-auth.md)
- [Preparation](courses/backend/node/week1/preparation.md)
- [Session Plan](courses/backend/node/week1/session-plan.md)
- [Assignment](courses/backend/node/week1/assignment.md)
- [Week 2](courses/backend/node/week2/README.md)
- [Session Materials](courses/backend/node/week2/session-materials/)
- [7. POST endpoints](courses/backend/node/week2/session-materials/07-post-endpoint.md)
- [8. PUT endpoints](courses/backend/node/week2/session-materials/08-put-endpoint.md)
- [9. DELETE endpoints](courses/backend/node/week2/session-materials/09-delete-endpoint.md)
- [Preparation](courses/backend/node/week2/preparation.md)
- [Session Plan](courses/backend/node/week2/session-plan.md)
- [Assignment](courses/backend/node/week2/assignment.md)
- [Final Backend Project](courses/backend/final-project/README.md)

- [Common Modules](shared-modules/README.md)
Expand Down
10 changes: 5 additions & 5 deletions courses/backend/node/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ This module is part of the Backend specialism and focuses on using Node.js to bu

## Contents

| Week | Topic | Preparation | Lesson Plan | Assignment |
| ---- | ------------------------ | ----------------------------------- | ----------------------------------- | ------------------------------------- |
| 1. | Express | [Preparation](week1/preparation.md) | [Assignment](./week1/assignment.md) | [Session plan](week1/session-plan.md) |
| 2. | Database connection; API | [Preparation](week2/preparation.md) | [Assignment](./week1/assignment.md) | [Session plan](week2/session-plan.md) |
| Week | Topic | Preparation | Session Plan | Assignment |
| ---- | ------------------------ | ------------------------------------- | ----------------------------------------------------- | ----------------------------------- |
| 1. | Express | [Preparation](./week1/preparation.md) | [Session plan](./week1/session-plan.md) (for mentors) | [Assignment](./week1/assignment.md) |
| 2. | Database connection; API | [Preparation](./week2/preparation.md) | [Session plan](./week2/session-plan.md) (for mentors) | [Assignment](./week1/assignment.md) |

## Module Learning Goals

Expand All @@ -16,6 +16,6 @@ By the end of this module, you will be able to:
- [ ] Build web servers with Express.js
- [ ] Design and implement APIs using HTTP methods following REST principles
- [ ] Use middlewares for authentication, logging, and validation
- [ ] Test APIs using Postman
- [ ] Use logging and debugging tools to monitor and troubleshoot applications
- [ ] Connect to databases and implement CRUD operations
- [ ] Test APIs using Postman
4 changes: 2 additions & 2 deletions courses/backend/node/week1/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ In this session we will focus on Express.js, which is an application framework f
By the end of this session, you will be able to:

- [ ] Explain what Express is and describe why it is used for building backend applications.
- [ ] Implement routing in Express to handle different HTTP requests and endpoints.
- [ ] Implement routing in Express to handle `GET` HTTP requests, endpoints and parameters.
- [ ] Use logging and debugging tools to monitor and troubleshoot Node.js applications.
- [ ] Apply middleware functions in Express to process requests and responses.
- [ ] Use Postman to test and debug APIs you have built.
- [ ] Understand the importance of Authentication and various methods for implementing it.
2 changes: 1 addition & 1 deletion courses/backend/node/week1/session-materials/06-auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,4 +116,4 @@ It is left as an optional exercise to add the following routes:
- `PUT /api/snippets/:id` to update a snippet
- `DELETE /api/snippets/:id` to delete a snippet

Also, it could be a good idea to deny the request if the user making the request is not confirmed
Also, it could be a good idea to deny the request if the user making the request is not confirmed.
51 changes: 25 additions & 26 deletions courses/backend/node/week1/session-plan.md
Original file line number Diff line number Diff line change
@@ -1,31 +1,30 @@
# Session plan

## Session Outline

- Express
- What is Express (10 mins)
- [Live coding: setup a server](./session-materials/01-server.md)
- [Excercise: create a local project and database schema](./session-materials/02-schema.md)
- Routing in Express (20 mins)
- `app.use`
- `app.get`
- [Live coding: routing](#appget-vs-appuse)
- [Excercise: Setup routing](./session-materials/03-routing.md) (10 mins)
- URL parameters in Express (30 mins)
- [Explanation and live coding](#query-parameters-vs-url-parameters)
- [Excercise: connect to the database](./session-materials/04-database-connection.md)
- [Excercise: GET endpoints](./session-materials/05-get-endpoints.md)
- Route order (15 mins)
- [Live coding: why route order matters](#route-order)
- Logging and debugging
- Middleware (15 mins)
- [`next` method](https://expressjs.com/en/guide/using-middleware.html)
- Modifying `request` and `response`
- <https://fullstackopen.com/en/part3/node_js_and_express#express>
- [Live coding: basic middleware example](#middleware)
- Authentication (30 mins)
- [Authentication explanation](#authentication-explanation)
- [Excercise: implement authentication](./session-materials/06-auth.md)
## Session outline

- What is Express (10 mins)
- [Live coding: setup a server](./session-materials/01-server.md)
- [Excercise: create a local project and database schema](./session-materials/02-schema.md)
- Routing in Express (20 mins)
- `app.use`
- `app.get`
- [Live coding: routing](#appget-vs-appuse)
- [Excercise: Setup routing](./session-materials/03-routing.md) (10 mins)
- URL parameters in Express (30 mins)
- [Explanation and live coding](#query-parameters-vs-url-parameters)
- [Excercise: connect to the database](./session-materials/04-database-connection.md)
- [Excercise: GET endpoints](./session-materials/05-get-endpoints.md)
- Route order (15 mins)
- [Live coding: why route order matters](#route-order)
- Logging and debugging
- Middleware (15 mins)
- [`next` method](https://expressjs.com/en/guide/using-middleware.html)
- Modifying `request` and `response`
- <https://fullstackopen.com/en/part3/node_js_and_express#express>
- [Live coding: basic middleware example](#middleware)
- Authentication (30 mins)
- [Authentication explanation](#authentication-explanation)
- [Excercise: implement authentication](./session-materials/06-auth.md)

## Exercises

Expand Down
26 changes: 26 additions & 0 deletions courses/backend/node/week2/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Node (Week 2)

In this session we will focus on connecting to a database, building an API, and using Postman to test our API endpoints. We will also cover how to structure our code for better maintainability and scalability.

## Contents

- [Preparation](./preparation.md)
- [Session Plan](./session-plan.md) (for mentors)
- [Assignment](./assignment.md)

## Session Learning goals

By the end of this session, you will be able to:

- [ ] Manage advanced database interactions in your service
- [ ] Understand what Knex is and why to use it
- [ ] Set up connections to your database using Knex
- [ ] Execute `select`, `create`, `delete` and `update` queries using Knex Query Builder
- [ ] Implement all REST endpoints using Express
- [ ] `POST`, `DELETE`, `PUT`
- [ ] Use appropriate error handling to understand and debug issues
- [ ] Configure Postman for advanced backend development
- [ ] Creating collections and saving requests
- [ ] Set up multiple environments
- [ ] Managing secrets
- [ ] Create basic test suites
131 changes: 131 additions & 0 deletions courses/backend/node/week2/assignment.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# Assignment

You'll set up and work with your own version of a simple Contacts API.

It will start with one endpoint (and you will add more throughout the task):

- `GET /api/contacts`

This endpoint accepts a query parameter `sort`. Here's how it should be possible to use it:

- `GET /api/contacts?sort=first_name%20ASC`
- Sorts contacts by first name, ascending
- `GET /api/contacts?sort=last_name%20DESC`
- Sorts contacts by last name, descending

## Setup

1. Go to/create a `node/week2` directory in your `hyf-assignment` repo.
2. Create yourself a new node application
3. Create a database called `phonebook` with a `contacts` table, with the following schema and data:

```sql
CREATE TABLE `contacts` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`first_name` varchar(255) NOT NULL,
`last_name` varchar(255) NOT NULL,
`email` varchar(255) DEFAULT NULL,
`phone` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- Sample data
insert into contacts (id, first_name, last_name, email, phone) values (1, 'Selig', 'Matussov', '[email protected]', '176-630-4577');
insert into contacts (id, first_name, last_name, email, phone) values (2, 'Kenny', 'Yerrington', null, null);
insert into contacts (id, first_name, last_name, email, phone) values (3, 'Emilie', 'Gaitskell', null, null);
insert into contacts (id, first_name, last_name, email, phone) values (4, 'Jordon', 'Tokell', null, null);
insert into contacts (id, first_name, last_name, email, phone) values (5, 'Sallyann', 'Persse', '[email protected]', '219-157-2368');
insert into contacts (id, first_name, last_name, email, phone) values (6, 'Berri', 'Bulter', null, null);
insert into contacts (id, first_name, last_name, email, phone) values (7, 'Lanni', 'Ivanilov', '[email protected]', null);
insert into contacts (id, first_name, last_name, email, phone) values (8, 'Dagny', 'Milnthorpe', null, null);
insert into contacts (id, first_name, last_name, email, phone) values (9, 'Annadiane', 'Bansal', null, null);
insert into contacts (id, first_name, last_name, email, phone) values (10, 'Tawsha', 'Hackley', null, null);
insert into contacts (id, first_name, last_name, email, phone) values (11, 'Rubetta', 'Ozelton', null, null);
insert into contacts (id, first_name, last_name, email, phone) values (12, 'Charles', 'Boughey', '[email protected]', '605-358-5664');
insert into contacts (id, first_name, last_name, email, phone) values (13, 'Shantee', 'Robbe', null, null);
insert into contacts (id, first_name, last_name, email, phone) values (14, 'Gleda', 'Peat', null, null);
insert into contacts (id, first_name, last_name, email, phone) values (15, 'Arlinda', 'Ethersey', '[email protected]', '916-139-1300');
insert into contacts (id, first_name, last_name, email, phone) values (16, 'Armando', 'Meachem', '[email protected]', '631-442-5339');
insert into contacts (id, first_name, last_name, email, phone) values (17, 'Codi', 'Redhouse', null, '401-953-6897');
insert into contacts (id, first_name, last_name, email, phone) values (18, 'Ann', 'Buncombe', '[email protected]', '210-338-0748');
insert into contacts (id, first_name, last_name, email, phone) values (19, 'Louis', 'Matzkaitis', '[email protected]', '583-996-6979');
insert into contacts (id, first_name, last_name, email, phone) values (20, 'Jessey', 'Pala', null, null);
insert into contacts (id, first_name, last_name, email, phone) values (21, 'Archy', 'Scipsey', '[email protected]', '420-983-2426');
insert into contacts (id, first_name, last_name, email, phone) values (22, 'Benoit', 'Mould', '[email protected]', '271-217-9218');
insert into contacts (id, first_name, last_name, email, phone) values (23, 'Sherm', 'Girardey', '[email protected]', '916-999-2957');
insert into contacts (id, first_name, last_name, email, phone) values (24, 'Raquel', 'Mudge', '[email protected]', '789-830-7473');
insert into contacts (id, first_name, last_name, email, phone) values (25, 'Tabor', 'Reavey', null, null);
```

4. Set up Express and an Sqlite connection in your node application. In your knex instance, make sure to set: `multipleStatements: true` - this is important!

5. Make sure you have an API router under the `/api` path set up like so:

```js
app.use("/api", apiRouter);
```

6. Create a contacts router at `/contacts`, and attach it to your API router.
7. In your contacts API, create the following endpoint:

```js
contactsAPIRouter.get("/", async (req, res) => {
let query = knexInstance.select("*").from("contacts");

if ("sort" in req.query) {
const orderBy = req.query.sort.toString();
if (orderBy.length > 0) {
query = query.orderByRaw(orderBy);
}
}

console.log("SQL", query.toSQL().sql);

try {
const data = await query;
res.json({ data });
} catch (e) {
console.error(e);
res.status(500).json({ error: "Internal server error" });
}
});
```

## The tasks

### Task 1 - Solve the SQL injection
Copy link
Contributor

@adamblanchard adamblanchard Nov 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've expanded these tasks to mirror a bit more of the things learned in the session.

I'm not sure if it's too much to complete in a week (would appreciate some feedback on that), but at least conceptually it captures a nice little API and allows the trainees to practice the themes of today.

To be honest, the SQL injection part is the one that sticks out the most, since it's not covered in the session. But, i don't think it's a bad idea to cover it again since it was taught in foundation, and is a nice practice to apply a more theoretical learning to now a much more practical implementation. And we also touch on some "insecure" knex practices in the error handling live coding example, which this builds on.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think for assignment it is fine it's a lot of stuff. If it would be in class excecises, that can be tricky, but for home assignment it is good.

There is nice practice now of check-in during the week so if anything is unclear hopefully the trainees will flag that and it can be explained then :)


The current implementation of the `sort` query parameter has introduced an SQL injection vulnerability.

First, you should demonstrate the SQL injection and that, for instance, it is possible to drop/delete the `contacts` table with the `sort` query parameter. Capture this demonstration with a screen recording, and attach it to your PR when you submit your assignment.

After having demonstrated the SQL injection vulnerability, your task is then to fix the issue. Your solution should be solved in the `app.js` file only. While the the `multipleStatements: true` configuration you used enables this vulnerability, it should not be changed in your solution.

### Task 2 - Improve your API

Create two additional endpoints to enable the following functionality:

1. Create new contacts
2. Delete an existing contact

### Task 3 - Error handling

Update your endpoints with appropriate error handling. You should, at least, handle the following cases:

1. Successful requests
2. Incorrect requests (e.g. an incorrectly formatted sort request)
3. Server issues (e.g. a missing database table, or an offline database)
4. A catch all for any other errors

Remember to:

1. Return the appropriate HTTP code
2. Avoid sending any implementation or internal data to the client
3. Log an appropriate message so you can debug issues that occur in your service

### Task 3 - Postman

1. Create a Postman collection to capture some example requests with your new API.
2. Create a basic test suite that you can run to validate that everything is working correctly.

Share both a link to your Collection and a link to a test run showing your tests passing in your pull request.
6 changes: 6 additions & 0 deletions courses/backend/node/week2/preparation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Preparation

- [NodeJS Web API with KNEX and Express](https://www.youtube.com/watch?v=QNw9q4YXR4E) (15 min)
- <https://fullstackopen.com/en/part3/node_js_and_express#rest> up until the `The Visual Studio Code REST client` section (15 min)
- Familiarise yourself with the [Knex Cheatsheet](https://devhints.io/knex) (especially Select, Scheme and Modifying sections)
- <https://jsonplaceholder.typicode.com/> - Free API for testing and prototyping. (5 min)
Original file line number Diff line number Diff line change
@@ -1,34 +1,20 @@
# API
# POST endpoint

## `POST /api/snippets`

Let's start with a simplified version of the `POST /api/snippets` route. First we add the POST route to `api/snippets.js`:

```js
// Contents of api/snippets.js

import express from "express";
import knex from "../database.js";

const router = express.Router();

// GET /api/snippets
router.get("/", async (request, response) => {
// TODO
});
// ...

// POST /api/snippets
router.post("/", async (request, response) => {
// TODO
});

// TODO: GET /api/snippets/:id

export default router;
// ...
```

---

To be able to insert a row into the `snippets` table, we need to have data in the `users` table. Create a user and note what the user ID is.

The POST request we want to make will look something like this:
Expand All @@ -42,9 +28,9 @@ POST /api/snippets
}
```

---
### Exercise: Implement the POST endpoint

**Task:** when we now make a request like
When we now make a request like:

```text
POST /api/snippets
Expand All @@ -57,7 +43,4 @@ POST /api/snippets

you should insert a new row into the `snippets` table with the data from the request body.

Hints:

- [Insert with Knex](https://knexjs.org/guide/query-builder.html#insert)
- When creating a snippet we also need to specify a `user_id`. For now, you can just pass in the `user_id` in the request body (alongside the other snippet data)
When creating a snippet we also need to specify a `user_id`. For now, you can just pass in the `user_id` in the request body (alongside the other snippet data).
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# PUT endpoint

TODO
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# DELETE endpoint

TODO
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import express from "express";
import knex from "../database.js";

const router = express.Router();

// Example POST endpoint to add a new contact
router.post("/", async (request, response) => {
try {
console.log(response.body); // Server side log, for developers
const insertedContact = await knex("contacts").insert(response.body); // This could be insecure!
response.status(201).json(insertedContact); // 201 Created
} catch (error) {
console.error("Error inserting contact:", error); // Server side error, for developers
response
.status(500)
.json({ message: "Something went wrong on the server." }); // Client side error, for users. Avoid leaking database info.
}
});

export default router;
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import knex from "knex";

// Run "sqlite3 phonebook.sqlite3 < phonebook.sql" to create this database first
const dbFile = "./phonebook.sqlite3";

const knexInstance = knex({
client: "sqlite3",
connection: {
filename: dbFile,
},
});

export default knexInstance;
Loading