This repository explains basic Node & NPM concepts.
- 01 Architecture
- 02 Differences to Browser JS
- 03 NPM
- 04 Modules
- 05 Express
- 06 Todo Fullstack
- 99 Resources
In this chapter are the different parts of a basic node architecture explained.
Graphic of how a Node/NPM installation could look like:
Node is a Runtime for running JavaScript outside of a browser. It uses the V8 Engine, which is also used in Google Chrome (Chromium), to execute JS Code. It works event-driven which allows non-blocking operations and is a great use for data-intensive applications.
Due to its single thread architecture it is also easily scalable.
NPM is a package manager for managing private or public pieces of code in the Node environment. The packages are kept in the global npm registry (www.npmjs.com) or in a private third party registry. Existing packages can be installed in your project via the npm CLI, and new packages can be created and uploaded or linked in your project.
Useful commands:
npm init
creates a new NPM projectnpm install [package]
installs a packagenpm ci
installs all packages with specific versions listed in thepackage-lock.json
NVM can manage what versions of Node and NPM you want to install. It also allowes switching between versions.
Useful commands:
nvm list
shows what versions are installednvm list available
shows all versions that can be installednvm install [version]
installs a versionnvm use [version]
switches to a version
π Task Β | Β β Questions
Since there is no DOM in the node environment there will also be no window
object. This means that some objects which are present in the browser are not available in node (eg. window, document, alert, prompt, ...). Furthermore is the outmost this-context different from JS in browser, where it is the window object. In Node the this-context is the global
object which works a bit like window. This also allowes to have setTimeout
and setInterval
, which would otherwise be included in the window object, in Node.
Another object which is exists in the Node environment is process
. This object keeps information about the process that started that executes the JS code. This information includes for example arguments used when executing a JS file and has methods to stop/exit the process.
process.argv
list of arguments used to execute this JS codeprocess.exit()
method to stop the execution
Unlike browser JS, Node is actually able to access things like the file system. This allows for very useful serverside execution of JS, but will also be a bit more dangerous than regular browser JS. Node provides a built-in support for file system access through the fs
package.
Access to the file system can be done in either a blocking, or non-blocking manner. The fs
package includes some synchronous methods, although an asynchronous approach is recommanded. Asynchronous methods can either have callbacks, eg. writeFileAsync
, where a callback function must be provided, or can be used in promise-fashion, eg. writeFile
from promise part of the package, where you can listen to a promise, or even use the modern async/await standard.
You can install packages at any time, anywhere. But you will need to keep track of what packages you are using in your code, especially when collaborating with other teammates. When you create a new NPM project using npm init
you will be prompted some question about that project. After answering, or skiping, those you will be presented with a package.json
file. This file contains some meta information about the project.
Whenever you install a package now you must specify if this package is a standard dependency or only a dev dependency (eg. only used for building the distribution files). You can specify this during installation with the --save
or --save-dev
flag.
For example we want to use moment JS in our project and install it like this:
npm install --save moment
This will create:
- the
node_modules
folder, if not yet present, with the moment package folder - the
package-lock.json
file, if not yet present
In the package.json
there will be a new entry of this package as a dependency.
// package.json
"dependencies": {
"moment": "^2.29.1"
}
While in the package-lock.json
there are some additional information about the exact version of the package.
// package-lock.json
"node_modules/moment": {
"version": "2.29.1",
"resolved": "http://proget.inova.ch/npm/dev/moment/-/moment-2.29.1.tgz",
"integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==",
"license": "MIT",
"engines": {
"node": "*"
}
}
To install all dependecies of a project you can use npm install
without arguments in the project directory. It will install all dependencies specified in the package.json
. This wil potentially re-generate the package-lock.json
file with the installed versions. To make sure you have the same version of packages installed as your collaborators when setting up an existing project on your machine, you can use the npm ci
command, which will install the exact packages listed in the package-lock.json
. This command is also recommended for build pipelines.
Sometimes you will need to install a package globally. For example a package that provides a CLI, that should be accessible from anywhere on your machine. You can do so by providing the -g
flag when installing a dependency.
npm install -g @angular/cli
On the main registry www.npmjs.com are all sorts of packages listed ready for you to use. You can search for a package in the search bar, or get redirected from google to one of the packages detail page. On this page you will see the README.md file from github with instructions on how to use this package, the install command with the package name, plus some meta information. For selecting a package to use in production, it is important to check the weekly downloads and the version cycle/last published version (how often do new versions release). Below is a screenshot of the angular CLI package.
For cleaner JS code people tend to move parts of code to their own modules, which then can be imported and used. In Node there are two major types of modules supported.
CommonJS modules are the original approach of modularization in Node. The module can export code via the exports
variable in the following way:
// module.js
const foo = 'bar';
module.exports = foo;
// client.js
const foo = require('./module.js');
In the meantime the latest JavaScript versions (EcmaScript) provides a native way to create modules. Especially in TypeScript project this is the preferred way of managing modules. They are used in the following way:
// module.js
export const foo = 'bar';
// client.js
import { foo } from './module.js';
Attention: to make it work you will need to add "type": "module"
in the package.json
or use the .mjs
file extension. In the browser you will need the attribute type="module"
on your script tag.
Express is the basis of Node backends. It creates a webserver and you will be able to define routes and add middleware.
You can startup a new webserver by creating an "app"
and listening to a port.
const express = require('express')
const app = express()
const port = 3000;
// define routes and middleware ...
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
});
You can create routes for your app by selecting a method (GET
, POST
, PUT
, DELETE
, ..), adding the route name (eg. /home
) and creating a route handler which accepts Request
and Response
arguments.
app.get('/home', function (req, res) {
// define what happens in this route
});
You can respond in a route in various ways. You can send back content like text, html or json. You could also just send a status code.
// send text
res.send('Hello World');
// send HTML
res.send('<h1>Hello World</h1>');
// render a view
// this uses template engine middleware
// https://expressjs.com/en/guide/using-template-engines.html
res.render('home');
// send a status
res.sendStatus(400); // bad request
You can also receive data from a request to use on the backend. For example you can get data from route parameters or from the request body.
// get data from route parameters
app.get('/user/:id', function(req, res) {
// access http://localhost:3000/user/1
console.log(req.params) // {id: 1}
});
// get data from request body
app.post('/user', function(req, res) {
console.log(req.body); // {firstname: 'John', lastname: 'Smith'};
// you might need middleware to receive the request body
// eg. express.json(), express.urlencoded({extended: true})
});
To add more functionality to the express application you will use middleware. For example to render views or receive/parse the request body. Such middleware is commonly included using app.use()
before your route definitions.
app.use(express.json())
app.use(express.urlencoded({extended: true}))
More infos on using and writing middleware in the express docs.
The goal of the project is to create a full stack JavaScript/Node application, where you can manage todo items. You will use existing scaffolding to create:
- Express server as backend
- Angular app as frontend
- Node CLI
- https://www.tutorialspoint.com/nodejs/nodejs_event_loop.htm
- https://www.tutorialspoint.com/nodejs/nodejs_global_objects.htm
- https://www.youtube.com/watch?v=ENrzD9HAZK4
- https://v8.dev/
- https://www.npmjs.com/
- https://www.simform.com/blog/what-is-node-js/
- https://nodejs.org/api/fs.html
- https://medium.com/computed-comparisons/commonjs-vs-amd-vs-requirejs-vs-es6-modules-2e814b114a0b
- https://expressjs.com/
- https://developer.okta.com/blog/2019/06/18/command-line-app-with-nodejs
- https://www.codecademy.com/learn/learn-node-js