- Backlog and Scrum Board in Jira
- 0.1 Issue
- 0.1.1 Child Issue
- 0.2 UI Mock
- 0.3 Backlog to Sprint
- 0.1 Issue
- Web Development with HTML, CSS, and JavaScript
- 1.1 JavaScript
- 1.1.1 Conditionals & Comparators
- 1.1.1.1 == value and === value and data type
- 1.1.1.2 if else statement
- 1.1.1.3 !== not equal
- 1.1.1.4 logical operators (&, ||, !)
- 1.1.2 Objects
- 1.1.2.1 Acess and Modify Objects
- 1.1.3 Array
- 1.1.4 Loops
- 1.1.5 Built-in Functions
- 1.1.1 Conditionals & Comparators
- 1.2 HTML
- 1.2.1 HTML Elements
- 1.2.1.1 HTML Common Attributes
- 1.2.1.2 HTML Inline Elements
- 1.2.1.3 HTML Block Elements
- 1.2.2 HTML head tags
- 1.2.1 HTML Elements
- 1.3 CSS
- 1.3.1
<style>elements for Inline and Internal- 1.3.1.1 Selector
- 1.3.1.2 CSS Properties
- 1.3.2 Website Layout
- 1.3.2.1 Flexbox Positioning
- 1.3.3 Background Colors
- 1.3.4 Semantic Elements
- 1.3.1
- 1.4 Add JavaScript to HTML
- 1.4.1 OnClick Event
- 1.4.1.1 Write Create Post Form
- 1.4.1.2 Add buttons to the Form
- 1.4.2 Style Button on Hover
- 1.4.3 DOM - Document Object Model
- 1.4.4 Extract JS code into own JS file
- 1.4.1 OnClick Event
- 1.5 JavaScript Framework
- 1.5.1Framework vs Libraries
- 1.5.1.1Frameworks
- 1.5.1.2Libraries
- 1.5.2Version Scheme
- 1.5.3npm Registry
- 1.5.3.1Key Concepts for npm Registry
- 1.5.3.2Package Manager
- 1.5.4npm CLI
- 1.5.4.1Key Conceptys for npm Registry
- 1.5.4.2Package json
- 1.5.4.3Basic CLI commands
- 1.5.5 vue.js
- 1.5.5.1 Basic Components Required
- 1.5.5.2 Essential VueJS Dependencies
- 1.5.5.3 Vue Component Structure
- 1.5.5.4 Key VueJS Features
- 1.5.5.5 Transpiling with Vue CLI and Compiler sfc
- 1.5.1Framework vs Libraries
- 1.6 NodeJS
- 1.6.0 Localhost
- 1.6.1 Port
- 1.6.2 HTTP Communication
- 1.6.3 URL/URI
- 1.6.4 IP Addresses
- 1.6.5 JSON Data Exchange
- 1.6.6 NodeJS Backend Implementation
- 1.6.6.1 Middleware
- 1.6.6.2POST Send data to backend
- 1.6.6.3GET Frontend asks for data
- 1.6.6.4Combine VueJS and NodeJS with the Same Server
- 1.6.7 Common Debugging Tools
- 1.7 MongoDB
- 1.7.1 Database Type
- 1.7.2 MongoDB
- 1.7.3 Connect Backend to Database
- 1.1 JavaScript
- Docker with Wholde Project
- Unit of work
- Will be traced through the workflow from creation to completion
Types:
- story: also called "user story", requirement expressed using non-technical language, and it is usually a larger parent work item, which is broken down to smaller tasks
- Task: a more technical description, more specific, describing a single unit of work
- Bug: a problem, which prevents product from function correctly
- Child issue sits below another issue e.g. a subtask
- A realistic representation or visual draft of the design of a website
- Reflects the app's design elements, like color scheme, images, typography, buttons
- Backlog collects all user stories or tasks or bugs, then review the list to prioritise the first 3
- Create Sprint to set timeline, plan the sprint and set the goal which is going to be done in the Sprint
- Development team decides how many tasks they can implement in 1 sprint
Jira Sprint and Backlog demo
== equality of value
=== equality of value and data type
var age = 30
age == 30
age == "30"
// the output will be true
age === "30"
// the output will be false, becuase the data types are not the samevar totalPrice = 19;
var shippingCost;
if (totalPrice > 20) {
shippingCost = 0
} else {
shippingCost = 5
}
//more conditions
if (totalPrice <= 10) {
shippingCost = 5
} else if (totalPrice <= 20) {
shippingCost = 3
} else {
shippingCost = 0
}if (userMembership !== "premium") {
// show user non-premium contents
// recommend premium upgrade
} else {
// show premium contents
}if (!(totalPrice > 20 || amazonPrime === true)) {
// calcualte the shipment cost
} else {
// free
}Data Type: Object
- Used to group related information
- Unordered collection of key-value pairs (also called "properties")
- object properties can be ANY data types
var person = {
firstName: "Ivy",
lastName: "Li"
}person.firstName
// modify objects
person.firstName = "Xiang"
// add new key-value pair
person.gender = "female"
// remove elements from objects
delete person.gender
// it will output true if successful
// more complicated key-value pair
person.friends = ["person1", "person2"]
person.currentWork = {
company: "example-company",
startDate: "someDate",
position: "data engineer"}
// combine array and objects together
var post1 = {content: "This is my super post", likes: 300, shares:20}
var post2 = {content: "This is my boring post", likes: 20, shares:0}
person.posts = [post1, post2]- Array is an ordered list of values
- Arrays are zero-indexed
var friends = ["person1", "person2"]
// access the value based on the index
friends[0]
// getting the array size
friends.length
// add a new element
friends[friends.length] = "person3"
// remove last element
friends.pop()- Looping is a basic programming concept
- One of the main building blocks of programming, which controls the flow of the program (like if / else statement)
- Nested Loop
var post1 = {
content: "This is my super post",
likes: 300,
shares:20
}
var post2 = {
content: "This is my boring post",
likes: 20,
shares:0
}
var facebookUser = {
userName: "IvyLi",
password: "secret",
email: "myemail@gmai.com",
profileImage: "reference-image",
friends: ["person1", "person2", "person3"],
posts: [post1, post2]
}
// go through each post and add new pair
for (index in facebookUser.posts) {
// do something
console.log(posts[index])
}
// for of loop grabs values directly while for in loop grabs index
for (post of facebookUser.posts) {
// replace likes to reactions
console.log(post.likes)
post.reactions = post.likes
delete post.likes
}- Custom Functions
- Built-in Functions: functions built into JavaScript
- Standalone (global) functions
- functions (methods) on objects
// global function
function replaceLikesWithReactions(user) {
for (post of user.posts) {
post.reactions = post.likes
delete post.likes
}
}
// call the function directly
var users = [facebookUserOne, facebookUserTwo]
for (user of users) {
user.interests = []
replaceLikesWithReactions(user)
}
// functions attached to objects
var facebookUserOne = {
userName: "Ivy",
password: "secret",
email: "myemail@gmail.com",
profileImage: "reference-image",
friends: ["person1", "person2", "person3"]
posts: [post1, post2],
greet: function() {
console.log("Hello Ivy")
}
}
// call the function from the object
facebookUserOne.greet()- Array
var friends = ["North", "Berth", "Lily"]
friends.pop()
friends.push("newFriend")- String
var someText = "When I type this sentence, the weather is great"
someText.replace(/great/g, "sunny") //regex for the first parameter, g means global - find all matches
var fullName = "Ivy Li"
// split the full name into first name and last name
fullName.split(/\s+/) // match any white space (\s), even one or more (+)- Example1: Get an array of objects with splitted names
var fullNames = ["John Doe", "Jane Doe", "John Smith", "Jane Smith"];
var firstAndLastNames = [] //Get an array of objects with splitted names
for (fullName of fullNames) {
var names = fullName.split(/\s+/)
var firstAndLastName = {
firstName: names[0],
lastName: names[1]
}
firstAndLastNames.push(firstAndLastName)
}- Example2: Calculate the age based on users' birthdate
Date object
- You can use the Date object and its methods to work with dates and times
- It's not a real built-in data type of JavaScript
function getAge(birthDateString) {
var today = new Date()
var birthDate = new Date(birthDateString)
var age = today.getFullYear() - birthDate.getFullYear()
var isSameMonth = today.getMonth() == birthDate.getMonth() // boolean object
var birthdayMonthInFuture = today.getMonth() < birthDate.getMonth()
var sameMonthDateInFuture = isSameMonth && today.getDate() < birthDate.getDate()
if (birthdayMonthInFuture || (sameMonthDateInFuture)) {
age--
}
// return the calculated age
return age // return also exits the function and is the end of the function
}- HTML stands for Hyper Text Markup Language
- Describe the structure of a website
- HTML is the most basic building block!
- HTML consists of different HTML elements, which I enclose or "mark up" the website content
- These HTML elements are set off from other text by "tags", which is element name surrounded by "<" and ">"
- HTML element is defined by a start tag, some content and an end tag
- All HTML elements can have attributes
- Attributes usually have name/value pairs
- Provide additional info about HTML elements
- id Attribute
- Used to specify a unique id for an HTML element
- Only 1 element with the same id can exist in an HTML document
- class Attribute
- Used to specify a name for an HTML element
- Multiple elements can share the same class
- Use
.to locate it in style for CSS
- Only take up as much width as necessary
- Text Element (Typography)
<b> <i>
<b>makes the text bold<i>display text in italic
- Links
<a>
- a means "anchor"
- Defines a hyperlink, used to link from one page to another
- href attribute specifies the URL of the page the link goes to
- An absolute URL
- A relative URL (like href="default.htm")
- Link to an element with a specified id within the page (href="#section2")
- Other protocols (like https://, ftp://, mailto:, file:, etc)
- A script (like href="javascript:alert('Hello');")
- Image and Media
<img/>
- Embed images
- Self-Closing Tags
- src attribute specifies the URL of the image
- An absolute URL
- A relative url - points to a file within a web site (like src="image.gif")
- Forms and Input
<button>
- Defines a clickable button
<input/>
- Specifies an input field where user can enter data
- Depending on type attribute the element is displayed in different ways
- type attribute
- default type is "text"
- placeholder attribute specifies short hint that describes teh expected value
- Take up full width
- Text Element (Typography)
<h1> - <h6>
- Used to define HTML headings
<h1>is most important heading,<h6>is the least
<p>
- To define a paragraph
- Browsers automatically add a single blank line before and after each
<p>element
- Lists
<ul> <ol> <li>
<ul>to define an unordered list<ol>to define an ordered list<li>to define a list item, used inside ordered or unordered list
- Layout
<div>
- Defines a division or section in an HTML document
- Used as a container for other HTML elements
- Semantic Elements
<nav>
<main>
<article>
<aside>
<!-- Main Content -->
<body>
<!-- Header Section -->
<header>
<h1><b>WELCOME TO MY WEBSITE</b></h1>
</header>
<!-- Input Section -->
<input type="text" placeholder="Enter username"/>
<button>Log In</button>
<!-- List Information -->
<div>
<h2>Name of the list</h2>
<h3><i>Short information on the list</i></h3>
<p><i>Written by Ivy</i> | <i>Date: Today</i></p>
</div>
<!-- Content Section -->
<div>
<img src="anime_time.jpg" width="200" height="200"/>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
<!-- Subsection -->
<div>
<h4>Section Header</h4>
<p>Paragraph to introduce this section</p>
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
<p>Paragraph to conclude this section</p>
<a href="https://xiangivyli.com">Click to see article</a>
</div>
</div>
</body><head>
- A container for metadata about the website
<title>
- Defines a title name for the webpage
- Shown in: the browser's title bar or when added to favorites
- Very important for search engine optimization (SEO)
- Use descriptive title, not just a list of words
<link>
- Used to define a link to an external resource
rel=specifies the relation of a linked resource- alternate
- author
- dns-prefetch
- help
- icon
- license
- next
- pingback
- preconnect
- prefetch
- preload
- prerender
- prev
- search
- stylesheet
- can be my own stylesheet
- Or use publicly available ones
href=specifies the location of the linked documenttype=define the media type of the linked document/resource- if I don't specify the attribute, the brower will look at the
relattribute to guess the correct type
- if I don't specify the attribute, the brower will look at the
Bootstrap A free CSS framework and it contains HTML, CSS and JS-based design templates, which I can use in my own website
- Favicon
- Short for "favorite icon"
- It's a small 16*16 pixel icon
- An icon associated with a particular website used on web browsers
<link rel="icon" type="image/svg+xml" href="favicon/favicon.svg">
<meta>
-
Used to specify the character set, page description, keywords, author of the document, and viewport settings
-
Will not be displayed on the page, but is used by browsers (how to display content or reload page), by seach engines (keywords)
- Define the character set used
- Define keywords for search engine
- Define a description of the web page
- Define the author of a page
- Refresh document every 30 seconds
- Set the viewport to make my website look good on all devices
<html>element
- The root element, holding all the content of an HTML document
- Tip: including the lang attribute to define the language of the web page (for search engine)
<!DOCTYPE>
- All HTML documents must start with it
- Tell browser that this is an HTML document including which HTML version
- HTML
- HTML+
- HTML 2.0
- HTML 3.2
- HTML 4.01
- HTML5
<!DOCTYPE html>
<html>
<!-- Head Section -->
<head>
<title>My First Website</title>
<link rel="stylesheet" href="style.css">
<link rel="icon" type="image/svg+xml" href="favicon/favicon.svg">
<link rel="icon" type="image/png" href="favicon/favicon-96x96.png">
<meta name="description" content="This is a test website">
<meta name="keywords" content="HTML, CSS, JavaScript">
<meta name="author" content="Ivy">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<!-- Main Content -->
<body>
</body>
</html>- CSS stands for Cascading Style Sheets
- Describe the style of a website
Three ways to apply CSS
- Inline CSS
- Applied directly to an HTML element using the style attribute.
<h1 style="color: blue; font-size: 2em;">Hello Ivy!</h1>
- Internal CSS
- Defined within a
<style>tag inside the of an HTML
<style>
h1 {
color: blue;
font-size: 2em;
}
</style>- External CSS
- Written in a seperate .css file and linked to the HTML document using a
<link>tag.
<link rel="stylesheet" href="style.css">
It is common to put <style> section in a seperate CSS file, the benefits of external CSS file:
- Better overview
- External CSS files could be reused for different HTML pages, in case of same styling
- External CSS files can be cached by the browser, which increases performance of the website
- Used to define style (CSS) information for a document
Step 1 WHICH element you want to style? Step 2 HOW to style it?
Each declaration includes a CSS property name and value
Text Styling
color: grey;font-size: 10px;- CSS Units
- CSS has several different units for expressiing a length
- Absolute Units
- Most import one: px
- Fixed, meaning not responsive to screen size changes
- Relative Units
-
Most important one: %
-
More dynamic
-
Responsive Web design
- The goal is to make web pages render well on a variety of devices and window or screen sizes
- When a website is responsive, it means it automatically adjusts for different screem sizes
-
Box Model
- for
divborder: 1px solid black;shorthand syntax for border-width, border-style, border-colorpadding: 10px;adds space inside the border (Used to create space around an element's content inside of any defined borders)margin: 10px;adds space outside the bordermargin-leftmargin-rightmargin-bottommargin-top
Display and Positioning
- Display and positioning properties control how elements are laid out on the page (1.3.2 address it)
- Define the horizontal alignment of the text in an element
.container {
display: flex;
justify-content: center;
align-items: center;
}
.absolute {
position: absolute;
top: 0;
left: 0;
}There are many different layouts, but the following structure is very common
- Header
- Navigation
- Main Section / Content / Aside
- Footer
These properties determine whether elements are block or inline, how they flow in relation to other elements, and where they are positioned in their container.
There are 2 common methods
-
Flexbox Layout
- Work with a flex container and flex items
- Makes it easier to deign flexible responsive designs
-
Grid Layout
- Grid-based layout system, with rows and columns
- Also makes it easier to design flexible web pages
The typical layout for a website
Parent container
The <div> is a flexible container
The flex container properties for flexbox (parent container) are: (these attributes will also be applied to children elements)
- flex-direction: horizontally (by default) or vertically (row, column, row-reverse, column-reverse)
- flex-wrap (nowrap, wrap)
- align-content
- align flex items vertically
- justify-content
- align flex items horizontally
- align-items (center, flex-start, flex-end, stretch, baseline)
#id-name {
display: flex;
}Attributes for flex items (children elements)
- order
- flex-grow
- Defines how much space it should take relative to the rest of the flex items
- Value is a number, default is 0
- flex-shrink
- flex-basis
- flex
- align-self
- background-color Attribute
- Colors are specified with predefined color names or RGB, HEX and some other values
- Elements with a meaning
<header>Element- Represents container for introductory content or navigational links
<article>Element- Should make sense on its own. Examples: forum, blog post, news story etc
<aside>Element- Defines content aside from the page content
- add
<script>in the<head> - Function invocation
- The code inside a function executes only when "something" invokes (calls) the function
-
- When it is invoked from JS code
-
- When an event occurs (like a user clicks on a button)
- Events are things that happen to HTML elements
- Something the brower does
- Something a user does
- And JavaScript can "react" on those events
The event occurs when the user clicks on the element
The form element is a container for different types of input elements
<form>
-
<label>- Defines a text label for the
<input>tag
- Defines a text label for the
-
<input>- Associate the
<label>with the<input> - To tie the elements together:
forattribute in label,idattribute of input field - if type is "email" or "password", the input should fit the format like @ or the password will be masked
<input type="email"><input type="password"> - HTML input types
- Associate the
-
<textarea>can wrap text- Define a multi-line text input field
- Usually used to collect user input like comments or reviews
-
<select>- Creates a drop-down list
-
<option>
<select name="category" id="category">
<option value="category1">Category 1</option>
<option value="category2">Category 2</option>
<option value="category3">Category 3</option>
</select>Some attributes for <form>
- required attribute: a boolean attribute, when present, it specifies that input field must be filled out before submitting the form
<div id="post-form-container">
<form>
<label for="title"><b>Title</b></label>
<input id="title" name="title of post" type="text" placeholder="Enter Title" required>
<label for="description"><b>Description</b></label>
<input id="description" name="description of post" type="text" placeholder="Add Description">
<label><b>Post</b></label>
<textarea type="text" placeholder="Your Content..." required></textarea>
<button id="hide-btn" onclick="hidePostForm()">Hide form</button>
<button id="save-btn">Save form</button>
</form>
</div>Some attributes for <form> style
- max-width: define the maximum width of an element, if content is larger, then max-width applies, if smaller, then the max-width has no effect
The logic should be the form shows up when click the "Create Post" button and hide the form when click the "Hide Form" button, the display:none will help the action.
Button 1: Show the form
<button onClick="handleCreatePost()">Create Post</button>function handleCreatePost() {
document.getElementById("post-form-container").style.display = "block";
}Button 2: Hide the form
<button id="hide-btn" onclick="hidePostForm()">Hide form</button>function hidePostForm() {
document.getElementById("post-form-container").style.display = "none";
}:hover Selector
- Used to select elements when you mouse over them
In the style.css file, the following code achieves
- the cursor becomes a pointer when hovering on the button
- the background of the button become green when hovering on the button
#login-search-section button:hover {
cursor: pointer;
background-color: rgb(71, 125, 71);
}Document Object
- Each HTML document that gets loaded into a window becomes a document object
- It's the root node of the HTML document, containing the contents of the page
Document Object Method
- With it I can access and modify all document content
document.getElementById("post-form-container")get div part from the whole document object, even can assign the div to a variable
var formEl = document.getElementById("post-form-container")1.1 document.getElementById("post-form-container").style
1.2 document.getElementById("post-form-container").style.display = "block"
can put it in the function and invoke it with button, change the display from "none" to "block"
<script>
function handleCreatePost() {
document.getElementById("post-form-container").style.display = "block";
}
</script>
<div id="login-search-wrapper">
<div id="login-search-section">
...
<button onClick="handleCreatePost()">Create Post</button>
</div>
</div>-
document.getElementByTagName("h4")get an array of this tag and can get the element by index -
document.createElement("p") -
document.createTextNode("some text")
var newParagraph = document.createElement("p")
var textEl = document.createTextNode("some text adding to new paragraph")
newParagraph.appendChild(textEl)<script src="javascript/app.js"></script>The complete template can be found in the post_website_template , including the favicon image folder, javascript folder, images, css and html files.
A JavaScript framework is a structured foundation of pre-written code that provides developers with a way to build web applications more efficiently. Think of it as a blueprint for constructing buildings - instead of designing everything from scratch, the proven patterns and components are ready to use.
- React.js (by Facebook)
- Component-based architecture
- Virtual DOM for performance
- Large ecosystem and community
- Vue.js
- Progressive framework
- Gentle learning curve
- Flexible integration options
- Angular.js (by Google)
- Full-featured framework
- Built-in tools and utilities
- Enterprise-level features
A whole guide or "framework"
- Date handling (moment.js)
- String manipulation (lodash)
- Form validation (validator.js)
- Date pickers
- Modal windows
- Navigation components
- Chart generation
- Data visualisation
- Mathematical operations
-
Include library in the application
Option 1: Public URL and reference it directly in the brower
<script src="https://unpkg.com/validator@latest/validator.min.js"></script>
Option 2: Download it with npm CLInpm i datepicker.jsand the package (module) will be in the node_modules folder
<script src="node_modules/validator/validator.min.js"></script>
Remember set the character encoding method<meta charset="UTF-8" /> -
Use the pre-defined function
Semantic Versioning
- Three-part version number
- Widely adopted version scheme
for 3.2.40
-
3 is the MAJOUR Version
- Big changes (features, bugfixes)
- Incompatible changes that affect apps that use older versions
-
2 is the MINOR Version
- Minor changes (features, bugfixes)
-
40 is the PATCH Version
- Usually only for bug fixes
- Which are backwards compatible
npm registry is a repository for JavaScript packages
- Registry = the service that makes all packages available (collection of repositories)
- Repository = the actual storage
The official website is npm
-
npm stands for Node Package Manager
-
npm is a package manager
-
Manages software packages
-
Can be used to install (download), upgrade, configure packages
-
npm was created as a package manager for Node and became standard way of managing packages for any JS package or JS framework
Package Discovery
- Search functionality
- Package information
- Download statistics
Version Management
- Semantic versioning
- Dependency resolution
- Update mechanisms
Security
- Vulnerability scanning
- Package signing
- Security advisories
Mac - Homebrew
Linux - apt
Windows - Chocolatey
Step 1: Install homwbrew
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"Actually, I can install Homebrew without administrator role. It installs Homebrew in your home directory ($HOME) rather than a system-wide location that typically requires sudo access.
mkdir -p $HOME/homebrew
curl -L https://github.com/Homebrew/brew/tarball/master | tar xz --strip 1 -C $HOME/homebrew
export PATH=$HOME/homebrew/bin:$PATHStep 2: Install npm
brew install node
Step 3: Install libraries
npm i validator@13.15.0When the MacOS takes the long time to install npm, there is nvm method to do so
Node Version Manager (NVM) is a tool for managing multiple versions of Node.js and npm.
Step 0: Run the Installation Script
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | bashStep 1: Create the .zshrc file
touch ~/.zshrcStep 2: Open the file
nano ~/.zshrcStep 3: Add NVM Initialisation Commands
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completionStep 4: Save and Exit, press Ctrl + X, then Y, then Enter to save and exit
Step 5: Source the file to reload
source ~/.zshrcStep 6: Install a Specific Node.js Version
nvm install 23Used to interact with npm registry (a vast library)
The npm Registry is the package repository for JS packages, and npm CLI is the CLI tool to interact with the registry to install, upgrade, remove, publish packages.
# Initialise a new project
npm init
# Install a package
npm install validator
# Install specific version
npm install validator@13.5.0
# Install package and save as development dependency
npm install --save-dev jest
# Remove a package
npm uninstall validator
# List installed packages
npm list
# Update packages
npm updateA configuration file used in Node.js projects to manage the metadata, dependencies, scripts, and configurations for the project, it acts as the central hub for defining and managing a project's setup, scripts, and external libraries
- Everytime the NPM CLI installs a package, the package.json updates automatically
- This automatic synchronisation ensures that the project's dependencies are always accurately documented
- When someone else clones the project, they can simply run npm install, and NPM CLI will read the package.json file and recreate the exact same dependency structure on their machine
{
"name": "teamable",
"version": "1.0.0",
"descirption": "Team member profile management application",
"dependencies": {
"validator": "^13.7.0",
"datepicker-js": "~0.1.1"
}
}There are packages required for the application to run in production:
- ^ means accept minor version updates (13.7.0 to 13.99.99)
- ~ means accept only patch updates (0.1.1 to 0.1.99)
- No prefix means use exactly this version
For example, express, lodash, moment
These are packages only needed during development, such as:
- Testing frameworks
- Build tools
- Development servers
- Linters and formatters
For example, jest, nodemon, webpack
Navigation Commands
# Show current directory
pwd
# List directory contents
ls
# Change directory
cd directory_name
# Go up one directory
cd ..
# Go to home directory
cd ~File and Directory Operations
# Create directory
mkdir new_directory
# Create file
touch new_file.txt
# Remove file
rm file.text
# Remove directory
rm -r directory_nameCopying and moving
# Copy file
cp source.txt destination.txt
# Move/rename file
mv old_name.txt new_name.txtFile Permissions
# View permissions
# r = 4, w = 2, x = 1
ls -l
-rw-r--r-- 1 user group file.txt
# Change permissions
# 6 is rw for owner, 4 is r for group, 4 is r for others
chmod 644 file.txt- Package.json: Defines project dependencies and scripts
- index.html: Contains a single root div element
- main.js: Entry point that connects Vue components to HTML
- .vue files: Components containing template, script, and style sections
- vue: The Vue.js framework itself
- core-js: For JavaScript version compatibility
- @vue/compiler-sfc: Compiles vue components
- @vue/cli-service: Provides development server and build tools
Find the package.json to check the dependencies here
Components split the UI into independent and reusable pieces, known as a Single-File Component
Three Main Sections:
<template>: Contains the HTML markup<script>: Contains component logic and data<style>: Contains component-specific CSS
<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>
<template>
<button @click="count++">You clicked me {{ count }} times.</button>
</template>- Components allow us to split the UI into independent and reusable pieces
- Allow us to think about each piece in isolation
- A Vue component needs to be "registered" so that Vue knows where to locate its implementation when it is encountered in a template
Step 1: Export Header with properties in the Header.vue
export default {
name: 'Header',
props: {
title: String,
action: String,
color: String,
bgColor: String
}
}Step 2: Import Header and export App with components in the App.vue
import Header from './Header.vue'
export default {
name: 'App',
components: {
Header
}
}Step 3: create the app instance and mount to div of index.html
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')- Use :attribute syntax for binding to HTML attributes
- Use @click instead of onclick
- Define methods in the methods section of component
- "@" is shorthand for "v-on"
- Attaches an event listener to the element
- One of a number of built-in special attributes (also called directives) that vue provides
- Start with the "v-" predix. Colon ":" is shorthand for "v-bind"
- Declare that "img" is a variable or reference rather than a string
<template>
<header>
<h1 :style="{ color: color }">{{ title }}</h1>
<button @click="handleClick" :style="{ backgroundColor: bgColor }">{{ action }}</button>
</header>
</template>
<script>
export default {
name: 'Header',
props: {
title: String,
action: String,
color: String,
bgColor: String
},
methods: {
handleClick() {
alert(`You clicked the ${this.action} button!`);
}
}
}
</script>
<style>
button {
background-color: aquamarine;
border: none;
color: white;
padding: 15px 32px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 4px 2px;
cursor: pointer;
border-radius: 12px;}
h1 {color: chocolate}
</style>- Used to declare the reactive state of a component. Return the initial state.
- It's a JS object that will be made reactive by Vue, when the function is called on creation of the component.
- Define data in the
data()function - Access component data using
thiskeyword in methods - Export components using
exportdefault
import image from "./team_background.jpg"
export default {
name: 'App',
data () {
return {
img: image,
}
},
}- One of a number of built-in special attributes (also called directives) that vue provides
- Start with the "v-" predix. Colon ":" is shorthand for "v-bind"
- Declare that "img" is a variable or reference rather than a string
<img :src="img" alt="Profile Background"/>- To bind the values
- Use {{ variableName }} for text interpolation
- Use v-model for two-way data binding with form inputs
- Updates the template whenever the model (data) changes and updates the model whenever the template changes
- It reduces the complexity of keeping user input consistent with the application data model
<span>Name: </span><b id="name">{{ name }}</b> //this is the template
<span>Name: </span>
<input type="text" v-model="name"/> //this is the model (data)
export default {
name: 'App',
data () {
return {
name: "Ivy Li",//this is the default value for the template
}
}}- Use v-show for toggling element visibility
- Controlled through boolean variables in component data
- Access component data using
thiskeyword in methods- "this" refers to the component instance
- All data object properties of the component can be accessed with "this"
<div v-show="!isEditMode">
export default {
name: 'App',
data () {
return {
isEditMode: false,//this is the default value for the template
}
},
methods: {
handleEditProfile () {
this.isEditMode = !this.isEditMode //"this" means
}
},
}- Break down large components into smaller, resusable ones
- Use component-scoped styling
- Keep components focused on a single responsibility
- Utilise Vue's built-in directives like v-model instead of manual DOM manipulation
- Follow Vue's naming convention (PascalCase for components)
- Clearly list all passed parameterised properties in the
propswith expected data type
- Compiles or transpiles Vue.js to vanilla JavaScript
- Starts a dev server and serves the application to the brower
Step 1: Structure the name of folders and locations of index.html, main.js, App.vue and Header.vue to match the default, cause the cli is looking for files in the folders with the default names
- node_modules: auto-generated folder, containing all installed libraries for the app
- src: source folder, it contains all the Vue.js components and JS files
- public: public folder,
.indexfile is located here as well as any other static assets
Step 2: Because the CLI is not installed in the operating system, use the location to start the binary file (CLI library)
node_modules/@vue/cli-service/bin/vue-cli-service.js serveOptions for Step 2: Add scripts in the package.json
- Convenient way to bundle common shell commands for your project
- Benefit: Commands are shared amongst everyone using the codebase, ensuring that everyone is using the same command with same configuration options
- npm knows where to find the vue-cli-service, so do not need to point out the route
"scripts": {
"serve": "vue-cli-service serve"
}And then npm run
npm run serveStep 3: Check the app with the local http: (http://localhost:8080/)
- Resource get requested via http
- Resource can come from my own computer (localhost) or from anywhere on the web
- Use npm run serve to start development server
- Automatic hot-reloading when files change
- Compilation of Vue files to brower-compatible JavaScript
- The hostname or the computer that is currently in use to run the program
- So it means "this computer" in a network
- Used for testing purposes in development, not in actual production
- Ports are virtual places within an operating system, where network connections start and end
- Every machine has a range of ports available
- These ports can be allocated to specific applications
- Every application or service, runs and is available on a specific port
Standard Port Port 80 is the standard port used by web servers
HTTP provides the foundation for frontend-backend communication:
- HTTP requests from frontend to backend are called "requests"
- HTTP responses from backend to frontend are called "responses"
- HTTP URLs consist of protocols (http/https), hostname, port, and endpoint
Important
HTTP Protocol
- System of rules that define how data is exchanged over internet
- HTTP stands for Hyper Text Transfer Protocol
- HTTP was originally created to send HTML files HTTPS is HTTP with encryption and verification
HTTP Response Status Codes
- Indicate whether a specific HTTP request has been successfully completed
- 200 OK
- Request succeeded
- 304 Not Modified
- Indicates to the brower that there is no need to retransmit the requested resources
- It's an implicit redirection to a cached resource
- 404 Not Found
- Server cannot find the requested resource
- 400 Bad Request
- HTTP Request must be fixed
- 500 Internal Server Error
- Some error on the server side
- Best to retry
- 200 OK
Uniform Resource Locator It is a reference to web resource, and combined with HTTP protocal, Hostname and File name (or port), or URI Uniform Resources Identifier
- Address of the server that is hosting the web application
- A host or website on the internet is identified by a host name
- Host names are mapped to IP addresses automatically
Note
- IP address is a unique address that identifies a device on the internet
- Every machine on the internet has an IP address
JSON serves as the universal language between frontend and backend:
-
JSON (JavaScript Object Notation) is the standard format for data exchange
-
All programming languages can understand and process JSON
-
JSON.parse()Convert to JS object -
JSON.stringify()Convert JS object into JSON string
NodeJS provides a powerful platform for building backend applications:
- Express.js is a popular NodeJS framework that simplifies backend development
- Backend serves frontend files (HTML, CSS, JS) when users first access the application
- The
require()function in NodeJS is equivalent toimportin frontend JavaScript
Express
- A web application framework
- Provides a set of features for web apps
- Helps to manage a server and routes
Sit between a request and a response, acting as a processing layer in the application workflow.
app.use(express.json())
app.use('/', express.static(_dirname + '/'))List to achieve
- index.html
HTTP Messages (Request and Responses) consist of Header and Body
- Metadata is in the HTTP Header
- "Payload" is the information or data sent in the HTTP message body
payload = {
task: inputValue
}
res = await fetch('/save-task', { //first parameter is the endpoint (URL)
method: 'POST', //indicate the desired action o be performed for a given resource
headers: {
'Accept': 'application/json', //we understand json language
'Content-Type': 'application/json' //we speak json language
},
body: JSON.stringify(payload) //transform payload to json format
})
josnRes = await res.json()
console.log(jsonRes);- server.js
- set the route with
express.static(__dirname + '/') - register middleware to parse JSON bodies
- set the http method is post
- grab data with
req.body - response sends frontend the log
- local server can also print the log
app.use(express.json()); //register middleware to parse JSON bodies
app.use('/', express.static(__dirname + '/'))
app.post('/save-task', function(req, res) {
const taskObj = req.body //get data from payload (body)
// next step is to connect to the database and save the task
console.log('Task received:', taskObj.task);
res.send({savedTask: taskObj.task}); // send back the response to claim the success
})step 1: grab data from input, and turn it to json format with stringify
step 2: set the endpoint (route) in the fetch() call at which the client can get access to it, endpoint is not a file path
step 3: set the method in the fetch() call means that Send data to the server (POST)
step 4: the sever can get data from the body, translate json to javascript object first with app.use(express.json()) and print it in the console of server and also send back a response for confirmation
step 5: when the server send back with the response, the frontend parses it as JSON with .json()
optional step 5: console.log() print the results
step 6: set as asynchronous with await, it will not block the frontend when fetch data, meanwhile, remember to set related parent function async as well
List to achieve
- index.html
async function loadTasks() {
res = await fetch('get-tasks', {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}})
jsonRes = await res.json();
// Load prepared tasks into the list
ulEl = document.getElementById('tasks');
for (task of jsonRes.tasks) {
const li = document.createElement('li');
li.textContent = task;
ulEl.appendChild(li);
}
}<body onload="loadTasks()"> //After loading the entire page, run the function- server.js
Set the same endpoint
get-tasksand usegetto provide some data like an array
app.get('/get-tasks', function(req, res) {
const tasks = [
"Write JS code",
"Write HTML code",
]
res.send({tasks: tasks}); //response sends the data and print it in the respoonse
})step 1: Set the endpoint on the server using app.get('/get-tasks'), defines where the client can request data from
step 2: On the frontend, use fetch() to make a request to that endpoint
step 3: The server sends back data using res.send()
step 4: The frontend read the data with res.json() to parse the response into a JavaScript object
step 5: Always makes the fetch call asynchronous, so it does not block the rest of the code, with await and async
Note
Backend endpoints define the API interface for frontend communication:
- Endpoints are URLs that handle specific frontend requests
- End endpoints serves a specific purpose (e.g., /get-profile, /update-profile)
The good thing is that it can use package.json together, add express in the package list then use build to compile vue.js to vanilla js which are the final static files (HTML, CSS, JS)
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"start": "node server.js"
}The compiled frontend files will be in the dist folder and can change the endpoint in the server.js, app.use('/', express.static(__dirname + '/dist'));
The second thing is to grab inital value from server.js and send it to app.vue
for the App.vue
Have a method nanmed
async fetchUserProfile() {
const res = await fetch('/get-profile')
return await res.json()
}and then created() call when load the full page
async created () { //is called when the component instance was created
const userData = await this.fetchUserProfile()
this.name = userData.name || ""
this.email = userData.email || ""
this.position = userData.position || ""
this.location = userData.location || ""
this.skills = userData.skills || ""
},for server.js, provide the data with send, always use the same endpoint
app.get('/get-profile', function(req, res) {
// Get data from the database
const response = {
name: "Ivy Li",
email: "xiangivyli@gmail.com",
position: "Data Engineer with full stack experience",
location: "Bristol, UK",
skills: "Problem Solving"
}
res.send(response);
});The third thing is to send updated data from Frontend to Backend.
For App.vue file
step 1 point the endpoint, and method is POST, imaging it is the post office ready to send out parcel
async updateUserProfile(payload) {
const res = await fetch('/update-profile', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify(payload)
})
return await res.json()
}step 2 where to get the parcel, using the update function to get payload, parameters are from the inputs, and also log the new data in console of browser
async handleUpdateProfile () {
const payload = {
name: this.name,
email: this.email,
position: this.position,
location: this.location,
skills: this.skills
}
const resJson = await this.updateUserProfile(payload)
console.log(resJson)
...}Browser Developer Tools
- Network tab for monitoring HTTP requests/responses
- Console for JavaScript errors and logs
- Source tab for debugging JavaScript code
Server-side logging
- Console.log statements in NodeJS
- Error handling and stack traces
- Request/response monitoring
API testing tools
- Postman for testing endpoints
- curl for command-line testing
- Brower extensions for API development
- Always validate data on the backend before processing
- Use appropriate HTTP methods for different operations
- Include proper error handling and status codes
- Implement security meansures:
- Use HTTPS for data encryption, Validate and sanitise all input data, Implement proper authentication/authorisation, Use enironment variables for sensitive configuration
- Keep frontend and backend concerns separate
- Use meaningful endpoint names and consistent API design
- Implement proper logging and monitoring
- Handle CORS (Cross-Origin Resource Sharing) appropriately
- Document your API endpoints and expected data formats
Key-Value Database
- Best for simple, fast data storage
- Store data as key-value pairs
- Primarily used as cache databases
- Limited in schema complexity
- Data stored in memory for quick access
For example: Redis, Memcached, etcd from Kubernetes (store cluster status in real-time)
Column Databases
- Suitable for handling unstructured data
- Values divided into multiple columns
- No predefined schema required
- Excellent for time series data and loT applications
- Easily scalable across multiple servers
For example: Cassandra, HBase
Document-Oriented Databases
- Perfect for general-purpose applications
- Store data in documents (JSON-like format)
- Schema-less structure
- Good for mobile apps, games, content management
- Faster read operations but slower updates
For example: MongoDB, DynamoDB, CouchDB
Relational Databases
- Most widely used database type
- Data organised in tables with rows and columns
- Requires strict schema upfront
- ACID Compliant (Atomic, Consistent, Isolated, Durable)
- Excellent for structured data and complex relationships
For example: MySQL, Postgresql, Cockroach DB (designed to solve the scalability issue)
Graph Databases
- Optimal for handling complex relationships
- Perfect for social networks, recommendation engines
- Uses edges to connect records directly
- Efficient for detecting patterns in data
For example: Neo4j, Dgraph
Search Databases
- Specialised for full-text search capabilities
- Creates indexes for efficient searching
- Often used alongside primary databases
- Optimised for fast search operations
For example: ElasticSearch, Solr
- NoSQL database
- Data stored in JSON format
- Schema-less design offers flexibility
- Default port is 27017
The list is:
- The
mongodserver - The
mongosshared cluster query router - The MongoDB Shell,
mongosh
Step 1: Install the MongoDB Shell, mognosh.
- MongoDB Shell Download from the official website: https://www.mongodb.com/try/download/shell, choose the platform
- Extract the ZIP file
cd ~/Downloads
unzip mongosh-2.5.3-darwin-arm64.zip- Move it to a Local Directory
mkdir -p ~/tools
mv mongosh-2.5.3-darwin-arm64 ~/tools/mongosh- Add
mongoshto PATH
nano ~/.zshrc- Add the PATH
export PATH="$HOME/tools/mongosh/bin:$PATH"- save and refresh
source ~/.zshrc- Test
mongoshYou will see the MongoDB Shell start up with a prompt like Current Mongosh Log ID:.... Connecting to: ...
Step 2: Install Docker with Colima
brewinstall
brew install colima docker- Start the
colima
colima start- Test docker
docker run hello-worldStep 3: Install MongoDB with Docker
- Pull the MongoDB Docker Image
docker pull mongodb/mongodb-community-server:latest- Run the Image as a Container, add command line options
docker run -d \
--name mongodb \
-p 27017:27017 \
mongodb/mongodb-community-server:latest \
--replSet myReplicaSet- Connect to the Mongodb Deployment with
mongosh
mongosh --port 27017- Validate the Deployment
db.runCommand(
{
hello: 1
}
)- View logs Directly from the Container
docker logs mongodbNote
However, we will connect to the Mongo database with the Node Backend, do not need the mongosh
- Each programming language has a library/module for DB connection
- Tell the library, WHICH DB to talk and HOW to AUTHENTICATE with that DB
For MongoDB
- Uses Mongodb Protocol for communication
- Connection string includes protocal, host, and port
- MongoDB Driver acts as a translator
MongoDB Node.js Driver
- Allow Node.js apps to connect to MongoDB and work with data
- MongoDB Node Driver works with Node or JavaScript specifically
Basic Operations
- CRUD operations (Create, Read, Update, Delete)
- Insert documents with
insertOne() - Update documents with
updateOne() - Query documents with
findOne() - Delete documents with
deleteOne()
Unique id Field
- Each document stored in a collection requires a unique_id field
- If not provided, the MongoDB driver automatically generates an id for the _id field
Best Practices for Managing Database Connections
- Connections should be closed after operations complete
- Use try-catch blocks for error handling
- Implement proper connection cleanup:
- Consider connection pooling for production applications
- Each request should ideally have its own connection lifecycle
- Avoid keeping connections open indefinitely
Add mongodb to package.json
Install the library
- Creates a new client that can talk to the Mongo server on the URL we provide
- We can use that client object and use its methods to connect and interact with the server
const url = "mongodb://localhost:27017"; // MongoDB connection string
const client = new MongoClient(url);Dockerise the node.js and mongodb can provide portable and stable features.
Step 1 install Homebrew without administrator role, it installs Homwbrew in your home directory ($HOME) rather than a system-wide location that typically requires sudo access
mkdir -p $HOME/homebrew
curl -L https://github.com/Homebrew/brew/tarball/master | tar xz --strip 1 -C $HOME/homebrewfor some systems like macOS, use .bash_profile instead of .bashrc
echo 'export PATH=$HOME/homebrew/bin:$PATH' >> ~/.bash_profile
source ~/.bash_profileStep 2 Install npm with nvm method
- Download nvm and install
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | bash- Create the .zshrc file
touch ~/.bash_profile- Open the file
nano ~/.bash_profile- Add NVM Initialisation Commands
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion- Save and Exit, press Ctrl + X, then Y, then Enter to save and exit
- Source the file to reload
source ~/.bash_profile- Install a specific Node.js Version
nvm install 23Step 3 Install docker, colima with brew
brewinstall
brew install colima docker docker-compose- Start the
colima, make the VM's IP address accessible from the host, and Docker containers should be reachable via forwarded ports
colima start --network-address - Test docker
docker run hello-worldStep 4 Build Node.js image with Dockerfile
- Multi-stage build: Keeps the final image lightweight.
- npm ci: Ensures clean and reproducible installs.
- Environment setup: PORT and EXPOSE are correctly configured.
- Copying only necessary files: Keeps the runtime image clean.
# Stage 1: Build the Vue app
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
RUN npm install -g @vue/cli
COPY . .
RUN npm run build
# Stage 2: Run the Node.js server
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
# Copy only the built files and server code, it is multi-staging build
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/server.js ./
# Set environment variables if needed
ENV PORT=3000
EXPOSE 3000
CMD ["node", "server.js"]Step 5 Install MongoDB and Set App with Docker-Compose.yml ✅ Services Overview
- MongoDB Uses the official mongo:latest image. Persists data using a named volume mongo-data. Always restarts if it crashes.
- App (teamable_demo_app) Built from a local Dockerfile. Maps port 3000 on the host to 3000 in the container. Mounts the current directory into the container at /app. Sets environment variables for: PORT MONGO_URL (points to the mongodb service) DB_NAME Depends on MongoDB and includes a health check using curl. ✅ Volumes mongo-data: Used to persist MongoDB data.
services:
mongodb:
container_name: mongodb
image: mongo:latest
restart: always
volumes:
- mongo-data:/data/db
- ./init-mongo.js:/docker-entrypoint-initdb.d/init-mongo.js:ro
app:
container_name: teamable_demo_app
build:
context: .
dockerfile: Dockerfile # Specifies the Dockerfile to use
ports:
- "3000:3000" # Map host port 3000 to container port 3000
volumes:
- .:/app # Mount the current directory to /app in the container
environment:
PORT: "3000"
MONGO_URL: "mongodb://mongodb:27017"
DB_NAME: "teamable_demo"
depends_on:
- mongodb
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000"]
interval: 1m30s
timeout: 30s
retries: 5
start_period: 30s
volumes:
mongo-data:Step 6 Add Artifact to .dockerignore
# Node modules and build artifacts
./node_modules
Dockerfile
.dockerignore
docker-compose.yml
# Build output
dist
build
# Evironment variables
.envdocker compose upDockerfileis to customise thenode.jsimagedocker-compose.ymldefines multi-containerspackage.jsondefines project metadata adn dependenciesserver.jsthe backend server entry point
Folders
distis the compiled artifact for vue.apppublicstatic files that are publicly accessiblesrcsource code for the frontendß
.
├── Dockerfile
├── dist
│ ├── img
│ │ └── team_background.16fa9cd1.jpg
│ ├── index.html
│ └── js
│ ├── app.js
│ └── chunk-vendors.js
├── docker-compose.yml
├── package-lock.json
├── package.json
├── public
│ └── index.html
├── server.js
└── src
├── App.vue
├── main.js
└── team_background.jpgExpress is
- A web application framework
- Provides a set of features of web apps
- Helps to manage a server and routes
const express = require('express') // to include a module in an app
const app = express() //return an Express application object, is stored in the variable `app`
app.listen(3000,function (){
console.log('app listening on port 3000')
}) //starts a server, listens for connections on the specified host and post, what should the app do when receiving the HTTP requesrvud-cli-service servecompiles the code first but also create a simple server to serve theindex.htmlfileexpresshas a functionusecan set endpoint (location) for index.html file
app.use('/', express.static(__dirname + '/dist')); //`/` is the very first context point with the backend, express.static() is a built-in function that **serves static files**, `__dirname` references the current directory to provide the absolute pathJSON.parse()converts to JS objectJSON.stringify()converts JS object into JSON string
Anytime when type into some data into the input field, the data can be transferred to backend via the network
Step 1: Create an area to accept the data in the index.html
index.html is entrypoint of every application
<label for="inputField">Input:</label> // define a caption, `for` connects it to a specific form element using that element's id
<input type="text" id="inputField" placeholder="Type something..."> // `id` is to uniquely identify, `name` is to label data
<button onClick="handleSaveTask()" id="submitButton">Save</button> // it has a function to handle received dataStep 2 Understand that the "Payload" is the information or data sent in the HTTP message body
const inputField = document.getElementById('inputField');
const taskText = inputField.value.trim();
payload = {
task: taskText
}Step 3 fetch() can send http request to backend
- first parameter is the endpoint
- second parameter is the object in which set the request method, GET, POST, OR OTHERS
- another function
JSON.stringify()in the object can convert a JavaScript object into a JSON string - another setting is about Request Headers to define the communication method is JSON
awaitsetsfetch()is asynchronous, and the parent function also needsasync- a "promise" is an object representing the eventual completion or failure of an asynchronous operation
- you will recieve a "promise" immediately, not the actual response
- with await keyword, the actual response is assigned to our variable once the request completes
res = await fetch ('save-task', {
method: 'POST',
body: JSON.stringify(payload)
headers: {
'Accept': 'application/json', // we understand JSON lanaguage
'Content-Type': 'application/json' // we speak JSON language
}
// Request Headers
})Step 4 Create middleware in the backend Middleware is a function that run before the final request handler
- Parse Data
- Authenticate users
- Log requests
- Handle errors
app.use(middleware)
app.use()let you register middleware or chain of middlewares- These are executed before the end route (app.get, app.post etc.)
- Every request goes through these middlewares
app.use(express.json()); // middleware to parse json bodiesStep 5 Create endpoints in the backend which is a web address (URL) at which the clients can gain access to it
- When the HTTP method is POST, the backend should also be POST
- first parameter is the path
- second parameter is handler which is the function executed when the route is matched
- a function, which is executed whenever a request is received on the specified path
- to handle the request, 2 parameters are provided: the request and response object
app.post('/save-task', function(req, res) { //this is an endpoint that listen for POST requests to /save-task
const taskObj = req.body // the body of request comes from the body: JSON.stringify(payload)
console.log('Task Recieved:', taskObj.task);
res.send({{saveTask: taskObj.task}}) // response is sent back to frontend
})Step 6 Translate the response from JSON to JS obejct
const jsonRes = await res.json(); //frontend recieves the response, translate to JS object
console.log(jsonRes); Step 1: Use fetch() to set the endpoints, methods and headers
async function loadTasks() {
const res = await fetch('get-tasks', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
})
}Step 2: Prepare Data and send out
let taskList = [
{id: 1, task: "Install postgresql"},
{id: 2, task:"Store data in postgresql"},
{id: 3, task:"Connect to postgresql from nodejs"},
]
app.get('/get-tasks', function(req, res) {
res.send({tasks: taskList}); //get2. send back the tasks to the frontend
})- The project name will use the folder name in which the docker-compose.yml located
- In a docker-compose.yml file, the db: section defines a "service", the name used inside the Docker Compose network
- postgres_db is the actual container name, if no setting, Docker auto-names it based on the project and service name
- Files in
/docker-entrypoint-initdb.dwill be executed automatically when the container starts for the first time, special behavior built into the official PostgreSQL Docker Image
services:
db:
container_name: postgres_db
image: postgres:latest
environment:
POSTGRES_USER: "postgres"
POSTGRES_PASSWORD: "password"
POSTGRES_DB: "teamable_demo"
ports:
- "5432:5432" # Map host port 5432 to container port 5432
restart: always
volumes:
- postgres_data:/var/lib/postgresql/data # Persist data in a named volume
- ./initdb:/docker-entrypoint-initdb.d # Initialize the database with a script
app:
container_name: teamable_demo_app
build:
context: .
dockerfile: Dockerfile # Specifies the Dockerfile to use
ports:
- "3000:3000" # Map host port 3000 to container port 3000
volumes:
- .:/app # Mount the current directory to /app in the container
environment:
DB_USER: "postgres"
DB_PASSWORD: "password"
DB_NAME: "teamable_demo"
DB_HOST: "db" # Use the service name as the host
DB_PORT: "5432"
depends_on:
- db
volumes:
postgres_data:- multi-stage builds to build vue app first then only include the /dist into the final lightweight image
- it only includes 1. Production dependencies, 2. /dist, 3. the server code (server.js)
npm cimeans "clean install", only installs exactly what is inpackage-lock.jsonfile, delete node_modules first
# Stage 1: Build the Vue app
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Stage 2: Run the Node.js server
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
# Copy only the built files and server code
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/server.js ./
# Set environment variables if needed
ENV PORT=3000
EXPOSE 3000
CMD ["node", "server.js"]{
"name": "teamable_demo",
"version": "0.1.0",
"dependencies": {
"vue": "3.5.17",
"@vue/cli-service": "5.0.8",
"@vue/compiler-sfc": "3.5.17",
"core-js": "3.43.0",
"express": "5.1.0",
"pg": "8.11.0"
},
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"start": "node server.js"
}
}Pool is a class from the pg (node-postgres) library that manages a pool of resuable database connections. Instead of opening a new connection for every query, a pool keeps a set of open connections and reuses them
const { pool } = require('pg') // the {} is destructuring assignment in JavaScript, only the Pool class from the pg module which can export multiple things- user
- host
- database
- password
- port
- send a SQL query to the database
- return a Promise (if using await)
- the result contains
- rows: the actual data
- rowCount: number of rows returned
- fields: metadata about the columns
app.get('/get-profile', async function(req, res) {
// Get data from the database
try {
const result = await client.query('SELECT * FROM profiles.user_info LIMIT 1');
res.json(result.rows[0]);
} catch (err) {
console.error('Error fetching profile:', err);
res.status(500).send({ error: "Failed to fetch profile" });
};
});The current project is using mongodb database
- Unit Tests: test individual functions/units in isolation, to make sure the logic of functions are correct
- Integration Test: test how multiple units work together, test integration between functions and systems
- UI/End-to-End Tests: test complete user flows through the application, simulate actual user interactions, Selenium or Puppeteer
Jest is the primary testing framework used for JavaScript applications
- Created by Facebook and open source
- Can test NodeJS, React, Vue and other JS frameworks
- Provides complete testing infrastructure and syntax
- validator.js
- export functions at the end
function isInvalidEmail(userObjECT) {
return !userObjECT.email.includes("@")
}
function isEmptyPayload(userObject) {
return Object.keys(userObject).length === 0
}
module.exports = {
isInvalidEmail,
isEmptyPayload
}- server.js
- import with destructuring
- makes it possible to unpack values from properties from objects or arrays into distinct variables
const { isInvalidEmail, isEmptyPayload } = require('./validator')- Name the testing files with same name of the file I want to test plus .test
- Jest uses "matchers" to let me test values in different ways
- There are different matchers available (.toBe()) is the matcher in the example
- Compare the output of the test with a value we expect the function to return
- Use commands to run test which is
jest
const { isInvalidEmail, isEmptyPayload } = require('../validator')
test('valid email', function() {
const testPayload = {
name: "test name",
email: "test.email@example.com",
interests: "testing"
}
const result = isInvalidEmail(testPayload)
expect(result).toBe(false)
})
test('invalid email', function() {
const testPayload = {
name: "test name",
email: "test.email",
interests: "testing"
}
const result = isInvalidEmail(testPayload)
expect(result).toBe(true)
})
test('empty payload', function() {
const testPayload = {}
const result = isEmptyPayload(testPayload)
expect(result).toBe(true)
})
test('non-empty payload', function() {
const testPayload = {
name: "test name",
email: "test.email@example.com",
interests: "testing"
}
const result = isEmptyPayload(testPayload)
expect(result).toBe(false)
})the expected output is Test Suites: 1 passed, 1 total Tests: 4 passed, 4 total
- Testing the interaction with the database or other services
- Simulate the brower/frontend
- "mocking"
- simulates the behaviour of a real method/object in controlled ways
- simulate sending an HTTP request to backend
requirements:
- stop the current app otherwise it will have conflict on the same port
- stop the node server
!!!
- The packaging process gets the software ready for deployment
- How you intend to transport the software (package into zip file or create an installer etc.)
Step 1: Build the lastest frontend
npm run build
Step 2: package.json
Step 3: Resulting File Type
- A zip is a set of files packages together into a single file and compressed using teh gzip compression program
- A tar is a set of files packaged together into a single file but not compressed
step 4: Exclude unnecessary file with .npmignore
- original frontend code
- test files
step 5: put dependencies only for development into devDependencies
{
"name": "teamable_demo",
"version": "0.1.0",
"dependencies": {
"vue": "3.5.17",
"core-js": "3.43.0",
"express": "5.1.0",
"mongodb": "5.7.0"
},
"devDependencies": {
"@vue/cli-service": "5.0.8",
"@vue/compiler-sfc": "3.5.17",
"jest":"30.0.4",
"supertest":"7.1.3"
},
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"start": "node server.js",
"test": "jest"
}
}Step 6: npm pack
- name
- version
- tgz
Step 7: Upload to a shared repo
Smaller Cloud Providers
- DigitalOcean
- Akamai
- linode
Common Services
- Storage
- Databases
- Servers
- Networking
- Software
Linux Distros are different versions of the Linux OS
Create a virtual server in DigitalOcean
- Public vs Private IP Address
- Public = Identifies the machine on the Internet
- Private = Identifies the machine on a local network. Unlike a public IP address, it can only be viewed by other devices on the local network
- SSH is a network protocal
- Works in the client-server model
- SSH runs on port 22 and provides secure terminal access to remote servers
- Basic SSH command structure:
ssh username@server_ip_address - File transfer using SCP (Secure Copy):
scp local_file username@server_ip_address:/destination/path
ssh root@server_public_ip_address- Close all ports by default
- Only open necessary ports
- Restrict access to sensitive ports (like SSH) to specific IP addresses
- Define which IP addresses can access that port
Common ports to configure
- SSH (22)
- HTTP (80)
- Application-specific ports (e.g. 3000 for node.js apps)
Rules
- Inbound rules (incoming traffic)
- Outbound rules (outgoing traffic)
Step 1: get my IP address https://www.whatsmyip.org/
Step 2: in the network, set the inbound rules to only accept my IP address
























