JavaScript Overview #
Java Script Language #
-
Programming language primarily used for creating interactive content in web browsers
-
Originally designed to run only in web browsers.
Use Cases:
- Client-side development, for example validating user input on a form.
Node.js #
-
Runtime environment that allows JavaScript to run outside of a browser, typically on a server.
-
Uses the V8 JavaScript engine (same engine used in Google Chrome) to execute JavaScript code on the server.
Use Cases:
-
Building web servers and APIs.
-
Server-side develpment / scripting.
-
Developing backend services.
-
Running JavaScript tools like npm (Node Package Manager).
Node Package Manager (npm) #
- Package manager, bundled with Node.js.
Yarn Package Manager #
-
Alternative package manager to npm.
-
Both npm and Yarn use the npm registry to get packages and package information.
Node Version Manager (NVM) #
-
Open-source version manager for Node.js.
-
Allows to install and manage different versions of Node.js and switch between them.
-
NVM stores the Node versions and associated modules inside your the directory.
JavaScript Setup (Linux Server) #
Node.js Installation (apt Version) #
Node.js Installation #
-
Node.js provides the runtime environment that allows to execute JavaScript code outside of a browser.
-
To use JavaScript on a server, it is not necessary to install Java.
# Install Node.js
sudo apt install -y nodejs
Verify Node.js Installation #
# Verify installation / list version
node -v
# Shell output:
v18.19.1
Node Package Manager (npm) Installation #
# Install npm
sudo apt install -y npm
# Verify npm installation
npm -v
# Shell output:
9.2.0
Node.js Installation (NVM Version) #
Node Version Manager (NVM) Installation #
# Find latest NVM version
https://github.com/nvm-sh/nvm
# Install NVM
wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
# Apply automatically added environment variables
source ~/.bashrc
# Verify installation / check version
nvm --version
# Shell output:
0.40.1
Node.js Installation #
# List available Node.js versions
nvm ls-remote
# Install Node.js: Latest version
nvm install node
# Shell output:
...
Now using node v23.3.0 (npm v10.9.0)
Creating default alias: default -> node (-> v23.3.0)
# Install Node.js: Specific version
nvm install 19
# Shell output:
Now using node v19.9.0 (npm v9.6.3)
List Installed Node.js Versions #
# List installed Node.js versions
nvm ls
# Shell output:
-> v19.9.0
v23.3.0
...
Switch Node.js Version: Current Shell #
# Use previously installed version 23
nvm use 23
# Shell output:
Now using node v23.3.0 (npm v10.9.0)
# Confirm currently used Node.js version
nvm current
# Shell output:
v23.3.0
Verify Node Package Manager (npm) Installation #
Node Package Manager (npm) is automatically included when Node.js installed via Node Version Manager (NVM).
# Verify npm installation
npm -v
# Shell output:
10.9.0
npm Commands Examples #
Create Project Folder #
# Create a directory for an example application
mkdir ~/example-app && cd ~/example-app
Initialize npm Project #
# Initialize the new npm project: Enter project information
npm init
# Shell output:
package name: (example-app)
version: (1.0.0)
description:
entry point: (index.js)
test command:
git repository:
keywords:
author:
license: (ISC)
About to write to /home/ubuntu/example-app/package.json:
{
"name": "example-app",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"description": ""
}
# Verify the newly created "package.json" file
ls
# Shell output:
package.json
Install & Uninstall Packages #
# Install project dependencies: All packages defined in "package.json"
npm install
# Install specific package: For example "Express web application framework"
npm install express
# Install specific package: Define package version
npm install express@4.17.1
# Install specific package: Globally / system wide
npm install -g express
Verify the updated “package.json” file:
# Cat package.json
cat package.json
# Shell output:
{
"name": "example-app",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"express": "^4.21.1" # Verify the express package
}
}
Uninstall a Package:
# Uninstall specific package: For example "Express web application framework"
npm uninstall express
Yarn Installation #
# Install Yarn
sudo npm install -g yarn
# Shell output:
added 1 package in 795ms
-g
Install package globally, make it vailable anywhere on the system, not just within a project
# Verify installation
yarn -v
# Shell output:
1.22.22
Node.js Example Application: HelloWorld #
# Create a Node.js example application
vi HelloWorld.js
// Outputs text to the console
console.log("Hi there, from Node.js");
# Run the Node.js application
node HelloWorld.js
# Shell output:
Hi there, from Node.js
Node.js Example Application: Simple Webserver #
Create Project Folder #
# Create a directory for an example application
mkdir ~/example-webserver && cd ~/example-webserver
Webserver Application #
Example 1: Simple Text Response #
Example 1: Simple text response on any requested url path
- example-webserver.js
// Import the 'http' module to create an HTTP server
import http from 'http';
// Define the port, using environment variable or default to 8080
const port = process.env.PORT || 8080;
// Create the HTTP server and define its behavior
const server = http.createServer((req, res) => {
// Send a simple text response to any incoming request
res.end('Hi there.\n');
});
// Start the server and listen on the specified port
server.listen(port, () => {
// Log a message to indicate the server is running
console.log(`Server listening on port ${port}`);
});
Example 2: Diverse Responses #
Example 2: Different responses regarding the requested URL path
- example-webserver.js
// Import the 'http' module to create an HTTP server
import http from 'http';
// Define the port, using environment variable or default to 8080
const port = process.env.PORT || 8080;
// Create the HTTP server and define its behavior
const server = http.createServer(function (req, res) {
// Send a simple text response
if (req.url === '/') return respondText(req, res)
// Send a json response
if (req.url === '/json') return respondJson(req, res)
// Send a 404 response
respondNotFound(req, res)
})
// Start the server and listen on the specified port
server.listen(port, () => {
// Log a message to indicate the server is running
console.log(`Server listening on port ${port}`);
});
// Send a simple text response
function respondText (req, res) {
res.setHeader('Content-Type', 'text/plain')
res.end('Hi there.\n')
}
// Send a json response
function respondJson (req, res) {
res.setHeader('Content-Type', 'application/json')
res.end(JSON.stringify({ text: 'Hi there.', numbers: [1, 2, 3] }))
}
// Send a 404 response
function respondNotFound (req, res) {
res.writeHead(404, { 'Content-Type': 'text/plain' })
res.end('Not Found\n')
}
Example 3: Stream Static Content #
Example 3: The following example streams static content from a directory named
public.
The file and folderstructure looks like this:
example-webserver
├── example-webserver.js
└── public
└── index.html
- example-webserver.js
// Import the 'http' module to create an HTTP server
import http from 'http';
// Import the filesystem module
import fs from 'fs'; // Import the filesystem module
// Import the querystring module
import querystring from 'querystring'
// Import utilities to simulate __dirname in ES modules
import { fileURLToPath } from 'url';
import path from 'path';
// Simulate __dirname in ES modules
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Define the port, using environment variable or default to 8080
const port = process.env.PORT || 8080;
// Create the HTTP server and define its behavior
const server = http.createServer(function (req, res) {
// Send a simple text response
if (req.url === '/') return respondText(req, res)
// Send a json response
if (req.url === '/json') return respondJson(req, res)
// Stream static files
if (req.url.match(/^\/static/)) return respondStatic(req, res)
// Send a 404 response
respondNotFound(req, res)
})
// Start the server and listen on the specified port
server.listen(port, () => {
// Log a message to indicate the server is running
console.log(`Server listening on port ${port}`);
});
// Send a simple text response
function respondText (req, res) {
res.setHeader('Content-Type', 'text/plain')
res.end('Hi there.\n')
}
// Send a json response
function respondJson (req, res) {
res.setHeader('Content-Type', 'application/json')
res.end(JSON.stringify({ text: 'Hi there.', numbers: [1, 2, 3] }))
}
// Stream static content from "public" directory
function respondStatic (req, res) {
// Construct the file path
const filename = `${__dirname}/public${req.url.split('/static')[1]}`
// Stream the file
fs.createReadStream(filename)
.on('error', () => respondNotFound(req, res))
.pipe(res)
}
// Send a 404 response
function respondNotFound (req, res) {
res.writeHead(404, { 'Content-Type': 'text/plain' })
res.end('Not Found\n')
}
- public/index.html
<!DOCTYPE html>
<html>
<head>
<title>jklug.work</title>
</head>
<body>
<h1>Hi there</h1>
<p>Some HTML content </p>
</body>
</html>
Example 3: Test the Webserver #
Run the Node.js application
# Run the application
node example-webserver.js
# Shell output:
Server listening on port 8080
Curl the different URL Paths in a new shell:
# Curl the webserver: Simple text response
curl localhost:8080/
# Shell output:
Hi there.
# Curl the webserver: Json response (Use browser for JSON output in a readable format)
curl localhost:8080/json
# Shell output:
{"text":"Hi there.","numbers":[1,2,3]}
# Curl the webserver: Stream static content
curl localhost:8080/static/index.html
# Shell output:
<!DOCTYPE html>
<html>
<head>
<title>jklug.work</title>
</head>
# Curl the webserver: Stream static content / Error not found
curl localhost:8080/static/djkgnsdg
# Shell output:
Not Found
# Curl the webserver: Not found response
curl localhost:8080/djkgnsdg
# Shell output:
Not Found
Node.js Example Application: Webserver with Express Framework #
GitHub Repository #
The code of this Node.js application is available on GitHub:
https://github.com/jueklu/node.js-webserver-with-express-framework
File And Folderstructure #
The file and folderstructure looks like this:
example-express-webserver
├── .env # Variable for Node.js webserver port
├── .gitignore
├── node_modules
├── package.json # Project metadata and dependencies
├── package-lock.json # Locked dependency versions
├── public # Static files (HTML, CSS, JS)
│ └── index.html # Example HTML file
├── src # Application source code
│ ├── app.js # Main application
│ ├── routes
│ │ └── index.js # Define route handlers
│ └── server.js # Main application entry-point
└── tests
└── app.test.js # Integration test
Create Project Folder #
# Create a directory for the example application
mkdir -p ~/example-express-webserver/{tests,public,src/routes} && cd ~/example-express-webserver
Initialize npm Project #
# Initialize the new npm project: Skip interactive prompts and use default values
npm init --yes
# Shell output:
{
"name": "example-express-webserver",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": ""
}
Install Express Package #
# Install Express package
npm install express
# Install dotenv package (loads environment variables)
npm install dotenv
# Install Jest and Supertest (for testing)
npm install --save-dev jest supertest
Application Files #
Webserver Main Block: app.js #
# Webserver main code block
vi src/app.js
// Import the Express framework for web applications
const express = require('express');
// Create an instance of the Express application
const app = express();
// Import routes
const routes = require('./routes');
// Import environment variables from .env
require('dotenv').config();
// Define the port, using an environment variable if available, or default to 80
const port = process.env.PORT || 80;
// Use routes from "src/routes/index.js"
app.use('/', routes);
// Export the app for testing and server setup
module.exports = { app, port };
Webserver Entry-Point: server.js #
# Starting block for the webserver
vi src/server.js
// Import the Express app instance and port configuration from app.js
const { app, port } = require('./app');
// Start the server
app.listen(port, () => console.log(`Server listening on port ${port}`));
Webserver Routes: index.js #
# Webserver routes
vi src/routes/index.js
// Import the built-in 'fs' (File System) module to work with files
const fs = require('fs');
// Import the Express framework for web applications
const express = require('express');
// Import the 'path' module for handling and manipulating file paths
const path = require('path');
// Create a router instance
const router = express.Router();
// Respond with plain text
router.get('/', (req, res) => {
res.send('Hi there.\n');
});
// Respond with JSON
router.get('/json', (req, res) => {
res.json({ text: 'Hi there.', numbers: [1, 2, 3] });
});
// Serve static files directly from the 'public' directory
router.use('/static', express.static(path.join(__dirname, '../../public')));
// Respond with custom 404 error for unmatched paths
router.use((req, res) => {
res.status(404).send('Not found\n');
});
// Export the router so it can be used in other files
module.exports = router;
Env File #
# Create .env file
vi .env
# Define webserver port
PORT=8080
HTML File #
# Create an example HTML file
vi public/index.html
<!DOCTYPE html>
<html>
<head>
<title>jklug.work</title>
</head>
<body>
<h1>Hi there</h1>
<p>Some HTML content </p>
</body>
</html>
Add Integration Test #
# Create a test file
vi tests/app.test.js
const request = require('supertest'); // Import Supertest to simulate HTTP requests
const { app } = require('../src/app'); // Import the app instance
describe('Basic Web Server Tests', () => {
it('should return "Hi there." for GET /', async () => {
const response = await request(app).get('/');
expect(response.status).toBe(200); // Expect a 200 OK status
expect(response.text).toBe('Hi there.\n'); // Expect the response body
});
it('should return JSON for GET /json', async () => {
const response = await request(app).get('/json');
expect(response.status).toBe(200); // Expect a 200 OK status
expect(response.body).toEqual({ text: 'Hi there.', numbers: [1, 2, 3] }); // Expect the JSON response
});
it('should return 404 for an unknown route', async () => {
const response = await request(app).get('/unknown');
expect(response.status).toBe(404); // Expect a 404 Not Found status
expect(response.text).toContain('Not found'); // Check the custom 404 message
});
});
Adopt package.json #
# Open package.json
vi package.json
Original file:
{
"name": "example-express-webserver",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"dotenv": "^16.4.5",
"express": "^4.21.1"
},
"devDependencies": {
"jest": "^29.7.0",
"supertest": "^7.0.0"
}
}
Add the Jest test and start script:
{
"name": "example-express-webserver",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "jest",
"start": "node src/server.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"dotenv": "^16.4.5",
"express": "^4.21.1"
},
"devDependencies": {
"jest": "^29.7.0",
"supertest": "^7.0.0"
}
}
Gitignore #
Exclude the node_modules
directory in version control.
# Create a .gitignore file
vi .gitignore
# .gitignore
node_modules/
Run the Just Integration Test #
# Run the Just test
npm test
# Shell output:
> example-express-webserver@1.0.0 test
> jest
PASS tests/app.test.js
Basic Web Server Tests
✓ should return "Hi there." for GET / (9 ms)
✓ should return JSON for GET /json (1 ms)
✓ should return 404 for an unknown route (1 ms)
Test Suites: 1 passed, 1 total
Tests: 3 passed, 3 total
Snapshots: 0 total
Time: 0.227 s, estimated 1 s
Ran all test suites.
Run the application #
# Run the application
node src/server.js
# Stop the application
Strg + c
Deploy Application via Container #
Dockerfile #
# Create a Dockerfile
vi Dockerfile
### Stage 1: Build
# Use the Node.js base image / Define as build stage
FROM node:23-slim AS build
# Set the working directory inside the container
WORKDIR /app
# Copy package.json and package-lock.json for dependency installation
COPY package*.json ./
# Install only production dependencies
RUN npm install --production
# Copy the rest of the application's source code
COPY . .
### Stage 2: Runtime
# Use the Alpine based Node.js image
FROM node:23-alpine3.20 AS runtime
# Create system user "appuser" / no PW
RUN adduser -S -D -H -h /app appuser
# Switch to "appuser"
USER appuser
# Set the working directory inside the container
WORKDIR /app
# Copy the application
COPY --from=build /app /app
# Expose the port your application listens on
EXPOSE 8080
# Run the application
ENTRYPOINT ["npm", "start"]
Build Docker Image #
# Build the Docker image
docker build -t example-express-webserver .
Verify Docker Image #
# List images
docker images
# Shell output:
REPOSITORY TAG IMAGE ID CREATED SIZE
example-express-webserver latest 3235c2482777 2 seconds ago 186MB
Run Docker Container #
# Run the Docker container
docker run -d -p 8080:8080 example-express-webserver
Test the Application #
Curl the different URL Paths in a new shell:
# Curl the webserver: Simple text response
curl localhost:8080/
# Shell output:
Hi there.
# Curl the webserver: Json response (Use browser for JSON output in a readable format)
curl localhost:8080/json
# Shell output:
{"text":"Hi there.","numbers":[1,2,3]}
# Curl the webserver: Stream static content
curl localhost:8080/static/index.html
# Shell output:
<!DOCTYPE html>
<html>
<head>
<title>jklug.work</title>
</head>
# Curl the webserver: Stream static content / Error not found
curl localhost:8080/static/djkgnsdg
# Shell output:
Not Found
# Curl the webserver: Not found response
curl localhost:8080/djkgnsdg
# Shell output:
Not Found
Links #
# Official Documentation
https://nodejs.org/api/index.html