Full Stack Open Course
Quote
In this part, we will familiarize ourselves with the practicalities of taking the course. After that, we will have an overview of the basics of web development and also talk about the advances in web application development during the last few decades.
Introduction to modern web development for single-page applications implemented with React and supported with RESTful and GraphQL web servers which are implemented with NodeJS.
In summary the technologies covered by this course will include:
- JavaScript
- RESTful
- GraphQL
- TypeScript
- React Native
Additional Topics:
- Continuous Integration
- Debugging Applications
- Container Technology
- Configuration
- Managing Runtime environments and databases
- Databases
The course guide states that all materials are meant to be read one part at a time and in order.
Note
The course material contains enough content so that every exercise can be solved as they are encountered. It is also beneficial to read all the materials in the part before attempting the exercise.
Course Materials can be found on GitHub for each part.
Parts of the course will increment exercises together to bring one larger application a small part at a time.
Quote
One part corresponds loosely to one week (averaging 15-20 hours) of studying, but the speed of completing the course is flexible.
Built around the idea of Mastery Learning therefore proceeding to the next part is conditional to the amount of completed exercises.
Mandatory Parts:
- Parts 1 - 4 are expected for all exercises that are not marked with *
(Which count towards a final grade)
Skipping the mandatory exercises for the current part, does not result in being unable to do the next part's mandatory exercises.
Full Stack studies consist of the core course and multiple extensions.
Parts 0-5 (core course) - Full Stack Web Development (5 credits)
Quote
The number of credits and the grade for the course are based on the total number of submitted exercises for parts 0-7 (including exercises marked with an asterisk).
Once enough exercises have been completed for a passing grade, the certificate can be downloaded from the submission system.
Credits and grades are calculated as follows:
exercises | credits | grade |
---|---|---|
138 | 7 | 5 |
127 | 6 | 5 |
116 | 5 | 5 |
105 | 5 | 4 |
94 | 5 | 3 |
83 | 5 | 2 |
72 | 5 | 1 |
How to study the course – instructions in a nutshell: 5 cr core course.
- Do the exercises. The exercises are submitted through GitHub and marking them as done on the submission system.
- The course certificate will be available in the submission system.
- Want to get University of Helsinki credits
- Enroll in the course. Will receive the enrollment link through the submission system once enough exercises have been completed. Read more here
- Save the student number. After course enrollment, save the University of Helsinki student ID number in the submission system.
- Do the online exam in the submission system. Read more here
- Mark the course completed in the submission system. Read more here
Exericses are submitted through GitHub and marked as done with the My Submissions
tab of the submission application.
Full-Stack Open Submissions Application
To access the courses submission application, I had to authorise FullStack to access only the publicly accessible data regarding my GitHub profile. This is to handle the submissions and grading.
If submitting exercises from different parts to the same repository use an appropriate name for directories.
If using a private repository, add mluukkai as a collaborator.
Exercises are submitted one part at a time. The number of exercises completed for that module are selected, and once submitted is final.
Submission being final means that no more exercises for that part can be submitted.
Quote
A system for detecting plagiarism is used to check exercises submitted to GitHub. If code is found from model answers or multiple students hand in the same code, the situation is handled according to the policy on plagiarism.
For official university credits, the course exam (covering parts 1-5) must be completed.
- In the event of failure, the exam can be retried after one week.
- Submissions may continue after the exam.
The exam is done in the exercise submission system.
Follow the instructions below to complete the exam.
- Enrol in the course through Open University.
- You will get the Enrolment link through the submission system once you have completed enough exercises.
Course Exam Explanation 1
Course Exam Explanation 2
Course Exam Explanation 3
The Chrome Browser for the course as it provides the best tools for web development, alternatively is FireFox Developer Edition
Git MUST be installed as exercises are verified through GitHub
Install sensible IDE Visual Studio Code Recommended.
It is recommended to NOT use Notepad, Nano, or GEdit.
Install Node.js (Parts 1-5
- using version 22.3.0. Part 10
- using version 20.11.0. Part 11 - End
- using version 18.13.0)
npm
(Node Package Manager) andnpx
(Node Package Execute) is automatically installed with Node.js.
Before programming the course will cover the basic principles of web development by examining the following example application.
The application exists to demonstrate the basic concepts of the course and is NOT an example of a modern web application, it merely demonstrates the older techniques of web development which could even be considered bad practice nowadays.
The 1st rule of web development is to always keep the Developer Console open.
MacOS - fn - F12
or option-cmd-i
Windows - fn - F12
or ctrl-shift-i
Make sure the Network
tab is open during web development.
Ensure cache is disabled (Ensuring the latest resources are being pulled from the web server.
There are two other settings of interest:
- Preserve Log - Useful in logging printed by the application when the page is reloaded.
- Hide Extension URLs - Hides requests of any extensions installed in the browser.
Additional Web Filters
Server and web browser communicate with each other using the HyperText Transfer Protocol (HTTP).
Another relevant note: HyperText Transfer Protocol Secure (HTTPS).
Reloading the example webpage with the network tab enabled reveals the following requests.
Network Requests Loaded upon visiting example application
Order of Network Requests
The sort is set to
Waterfall
meaning it follows the descending order of requests by client and responses from the server.
sequenceDiagram
participant C as Client
participant S as Server
%%http_content_request [Request 1 - HTTP Document]
C ->> S: HTTP GET https://studies.cs.helsinki.fi/exampleapp/
S -->> C: Return HTML Document
%%js_content_request [Request 2 - JavaScript Script]
C ->> S: HTTP GET https://studies.cs.helsinki.fi/Snipped-Endpoint
S -->> C: Return JavaScript Script
%%img_content_request [Request 3 - Image]
C ->> S: HTTP GET https://studies.cs.helsinki.fi/exampleapp/kuva.png
S -->> C: Return kuva.png Image
GET /exampleapp/ HTTP/1.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: en-GB,en;q=0.9
Cache-Control: no-cache
Connection: keep-alive Cookie: c83c27d4fad704d2abcc94600fc20e48=
Host: studies.cs.helsinki.fi
Pragma: no-cache
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: cross-site
Sec-Fetch-User: ?1
Sec-GPC: 1 Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36 sec-ch-ua: "Chromium";v="140", "Not=A?Brand";v="24", "Brave";v="140"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
HTTP/1.1 200 OK
Connection: close
Content-Length: 487
Cache-Control: no-cache, no-store, must-revalidate
Content-Type: text/html; charset=utf-8
Date: Thu, 18 Sep 2025 20:59:48 GMT
Etag: W/"141-T/danxJCVua08jxAk4P+sdtBvQY"
Expires: 0
Pragma: no-cache
Server: nginx/1.20.1
X-Powered-By: Express
<!DOCTYPE html>
<html>
<head><script type='text/javascript' src='https://studies.cs.helsinki.fi/bRGOnOjPS2ms48ZB94NC4eVeo7fXhxBmqPdBb4hsPHJs3taEpMb23v8tRFKzsfeomeWZmK1G-tMl_qyx4a4GRQ=='></script>
</head>
<body>
<div class='container'>
<h1>Full stack example app</h1>
<p>number of notes created 100</p>
<a href='/exampleapp/notes'>notes</a>
<img src='kuva.png' width='200' />
</div>
</body>
</html>
The response headers contain vital information regarding how the server responded to the request, some key pieces include:
- Content-Type
- Informs the response is a txt file in the utf-8 format, and the contents have been formatted with HTML
- Content-Length
- The length of the response in bytes
Relevant Note: HTTP Headers
Once the HTTP content is downloaded onto the client's browser the script request is made to pull a script from a studies.cs.helsinki.fi
endpoint, after this occurs the next request of interest is made being the image request.
GET /exampleapp/kuva.png
HTTP/1.1 Accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: en-GB,en;q=0.9
Cache-Control: no-cache
Connection: keep-alive
Cookie: c83c27d4fad704d2abcc94600fc20e48=
Host: studies.cs.helsinki.fi
Pragma: no-cache
Referer: https://studies.cs.helsinki.fi/exampleapp/
Sec-Fetch-Dest: image
Sec-Fetch-Mode: no-cors
Sec-Fetch-Site: same-origin
Sec-GPC: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36 sec-ch-ua: "Chromium";v="140", "Not=A?Brand";v="24", "Brave";v="140"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
HTTP/1.1 200 OK
server: nginx/1.20.1
date: Thu, 18 Sep 2025 21:39:57 GMT
content-type: image/png
content-length: 89350
x-powered-by: Express
accept-ranges: bytes
cache-control: public, max-age=0
last-modified: Fri, 28 Aug 2020 06:06:34 GMT
etag: W/"15d06-17433acc210"
The example website works as a traditional web application, when entering the page, the browser fetches the HTML document which details the structure of the website content.
Web Servers depending on their internal programming can also dynamically generate HTML for example utilising data from a database.
const getFrontPageHtml = noteCount => {
return `
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div class='container'>
<h1>Full stack example app</h1>
<p>number of notes created ${noteCount}</p>
<a href='/notes'>notes</a>
<img src='kuva.png' width='200' />
</div>
</body>
</html>
`
}
app.get('/', (req, res) => {
const page = getFrontPageHtml(notes.length)
res.send(page)
})
Writing HTML alongside the code is not the smartest course of action to accomplish dynamic pages but it was widely utilised by PHP developers.
Web Servers can be created with some of the following:
- Java Spring
- Python Flask
- Ruby on Rails
- Express with Node.js
Notes HTML Document Response Body & Rendered page
Despite having an obvious list of notes displayed the HTML code does not contain any list of notes, despite the rendered page clearly having them. The cause of this is the script in the <head>
section
The fourth request listed in the screenshot indicates a new HTTP GET Request to the web server to download the JavaScript for main.js
.
// main.js script
var xhttp = new XMLHttpRequest()
xhttp.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
const data = JSON.parse(this.responseText)
console.log(data)
var ul = document.createElement('ul')
ul.setAttribute('class', 'notes')
data.forEach(function(note){
var li = document.createElement('li')
ul.appendChild(li);
li.appendChild(document.createTextNode(note.content))
})
document.getElementById("notes").appendChild(ul)
}
}
xhttp.open("GET", "/exampleapp/data.json", true)
xhttp.send()
The fourth request shown in the screenshot corresponds to the browser fetching /exampleapp/main.js
, which was triggered by the <script>
tag in the HTML. After downloading the JSON data, the browser executes this JavaScript to dynamically render the notes.
The data for the notes is pulled from the
data.json
file on the web server, which shows the JSON stored data.
Chromium-based
browsers are not effective for displaying JSON data, as a set plugins like JSONView can be installed to improve the development experience.
The console.log(data)
line in the JavaScript indicates to the browser to output to the Dev Tools Console the contents of the data.json it received.
Console Outputted Notes Data
In the event of the JavaScript above onreadystatechange
is the an xhttp object event handler.
The
readystatechange
event is fired whenever thereadyState
property of theXMLHttpRequest
changes.
Quote
The function code checks that the readyState equals 4 (which depicts the situation The operation is complete
) and that the HTTP status code of the response is 200 indicating a successful connection.
Event Handling functions in JavaScript are called
Callback
functions.
HTML pages can be seen as tree structures, which can be seen in the dev tool's Elements tab in the same structure.
DOM is an API that enables modification through programming of element trees.
var ul = document.createElement('ul');
data.forEach((note) =>
{
var li = document.createElement('li');
ul.appendChild(li);
li.appendChild(document.createTextNode(note.content))
});
document.getElementById('notes').appendChild(ul)
sequenceDiagram
participant D as Document
participant U as UL Element
participant L as LI Element
participant T as Text Node
participant N as Notes Element
D->>U: createElement('ul')
loop For each Note in data.json
D->>L: createElement('li')
U->>L: appendChild(li)
D->>T: createTextNode(note.content)
L->>T: appendChild(textNode)
end
D->>N: getElementById('notes')
N->>U: appendChild(ul)
list = document.getElementsByTagName('ul')[0]; // Get First Element with the tag `ul`
newElement = document.createElement('li');
newElement.textContent = "Client HTML Page manipulation is easy from the console";
list.appendChild(newElement); // Appends to the document's existing notes list.
The change is local; limited to the browser client, therefore once refreshed the
newElement
will disappear as it is not stored on the server-side.
The head element of the HTML code of the Notes page contains a link tag, which determines that the browser must fetch a CSS style sheet from the address main.css.
The file contains two Class Selectors. (.container
and .notes
)
Classes are attributes which can be added to a HTML Element.
HTML elements can also have other attributes apart from classes. The div element containing the notes has an id attribute. JavaScript code uses the id to find the element.
The Elements tab of the console can be used to change the styles of the elements.
Changes made on the console will not be permanent. To make lasting changes, they must be saved to the CSS style sheet on the server.
The notes page contains a <form>
element.
<form action='/exampleapp/new_note' method='POST'>
<input type="text" name="note"><br>
<input type="submit" value="Save">
</form>
This form takes a text for a new note and upon submitting makes a HTTP POST
request to push the new note onto the server.
The form tag has the attributes
action
&method
in this case define the location to send the HTTP POST request.
The network tab shows the data which is sent in the POST
request.
As is shown by the blue bullet point the text entered when save is clicked the POST request sends the data to the web server which stores it allowing all users to view it.
POST /exampleapp/new_note HTTP/1.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: en-GB,en;q=0.9
Cache-Control: no-cache
Connection: keep-alive
Content-Length: 13
Content-Type: application/x-www-form-urlencoded
Cookie: c83c27d4fad704d2abcc94600fc20e48=
Host: studies.cs.helsinki.fi
Origin: https://studies.cs.helsinki.fi
Pragma: no-cache
Referer: https://studies.cs.helsinki.fi/exampleapp/notes
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Sec-Fetch-User: ?1
Sec-GPC: 1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36
sec-ch-ua: "Chromium";v="140", "Not=A?Brand";v="24", "Brave";v="140"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
HTTP/1.1 302 Found
Content-Length: 244
Cache-Control: no-cache, no-store, must-revalidate
Content-Type: text/html; charset=utf-8
Date: Fri, 19 Sep 2025 23:24:21
GMT Expires: 0
Location: /exampleapp/notes
Pragma: no-cache
Server: nginx/1.20.1
Vary: Accept
X-Powered-By: Express
The POST request is sent and then the server responses with a
302 Redirect
indicating to the client to go to the location/exampleapp/notes
essentially refreshing the page for the user.
app.post('/new_note', (req, res) => {
notes.push({
content: req.body.note,
date: new Date(),
})
return res.redirect('/notes')
})
Quote
The server does not save new notes to a database, so new notes disappear when the server is restarted.
This adds the new note as JSON to an existing notes
array found at data.json
, and then provides to redirect the user back to the original notes page.
The new note data is sent in the
body
of the HTTP POST request.
Quote
The Notes page of the application follows an early-nineties style of web development and uses "Ajax". As such, it's on the crest of the wave of early 2000s web technology.
AJAX is a set of web development techniques which use a variety of technologies on the client-side to create asynchronous web applications, therefore allowing web applications to send and retrieve data from a server asynchronously without interrupting the display or pre-existing behaviour on the page.
Important
AJAX is a programming pattern.
- Requests are issued with the XMLHTTPRequest object using the JavaScript language.
Before AJAX all webpages were traditional web applications and all the HTML code that was presented was generated by the server.
Note
Submitting forms still uses the traditional method to submit web forms.
In an SPAs the logic is entirely server side and the browser/client merely renders the HTML presented.
The notes app example as assessed above, gives some of its responsibilities back to the browser, which requests and runs the JavaScript returned, issuing the retrieval of JSON data.