Node.js kursen uppdaterad
9 minuter i lästid Kursnyheter Nodejs

Node.js kursen uppdaterad

En s.k. disruptiv uppfinning, innebär ett nytillskott till en marknad eller annan form av intressegruppering, som i grunden förändrar spelreglerna, spelplanen och vilka aktörer som blir ledande.

Man kan utan tvekan hävda att när Ryan Dahl presenterade Node.js för första gången 2009, så var det innovativt men också disruptivt. Jag länkar till hans presentation den 8 november 2009, här nedan. Den är väl värd att se, än idag.

Fram till denna tidpunkt var JavaScript ett språk man programmerade i av ren nödvändighet, men begränsade sig i den utsträckning det var möjligt. Många ansåg (undertecknad inkluderad) att JavaScript inte var att betrakta som ett riktigt programspråk.

Ryan hade dock spanat in en egenskap vi andra missat. Genom att bland annat studera vad som gjorde att programspråket Erlang var så effektivt på att hantera många kommunikations- kanaler, insåg han att det var asynkron och icke-suspenderande in- och utmatning. (Äh..., vi tar det sista på engelska, asynchronous and non-blocking I/O)

JavaScripts exekveringsmodell var konstruerad, av Brendan Eich, runt en kö för anropsbara funktioner (eng. call-back queue). Händelser, såsom tangentbordsklick, musrörelse, eller HTTP respons med flera, alla resulterade i att en funktion (call-back) lades in i kön. När aktuell funktion exekverat färdigt på funktions-anrops-stacken (eng. call stack), hämtades nästa funktion från kön.

Eftersom Brendan skapat JS enbart för att exekvera i en webbläsare, krävdes en stor anpassning för att hantera vanligt förekommande uppgifter för ett program som exekverar på en server (eller laptop), såsom hantering av filer, processor, signaler med mera.

Resultatet blev en smart kombination av tre separata beståndsdelar.

  • Google V8 JavaScript Engine
  • C biblioteket libuv för avancerad händelse hantering (eng. event loop)
  • Linux system-anrop (egentligen POSIX)

Kopplingen till systemanropen, kan man fortfarande se i dokumentationen, för t.ex. open() respektive stat(). Två systemanrop i *NIX, som är mer eller mindre direkt replikerade i Node.js Core API.

OK, vad hände sen då? Ett system för att söka, hantera och installera Node.js program från en central plats, skapades av Isaac Z. Schlueter ett år senare och lade grunden för att snabbt sprida bibliotek och program. Han skapade NPM (Node Package Manager). I början brukade han hävda att NPM stod för Npm is not a Package Manager. Plankat från GNU, som ju står för Gnu is Not Unix.

Plötsligt såg världen en stor mängd program, bibliotek och ramverk som var visslande snabba, vilket givetvis lockade till sig fler intressenter och det blev, vad som brukar kallas, en snöbollseffekt.

Snabbspolar vi drygt tio år senare, så har verktyg och metodik för utveckling av webbapplikationer i grunden fullständigt förändrats; och det helt till det bättre.

Populariteten för språket JavaScript har också ändrats, eftersom många nya programmerare kom in och började efterfråga funktionalitet som fanns i andra språk. Idag, så utgör Modern JavaScript (eller ECMAScript som det formellt heter) ett mycket angenämt språk att programmera i.

Mycket av just den utvecklingen har vi sett under de senaste fem, sex åren, med start i ES2015 och en ny specifikation/version varje år. Vi har fått lambdauttryck, destrukturering av sammansatta data, array streams, promises, async functions och mycket mer.

Google har varit snabba med att följa nya specifikationer och implementerat dessa i V8 för att göra dessa tillgängliga i webbläsaren Chrome. Eftersom även Node.js använder V8, så har man kunnat hålla sig uppdaterad med ES utveckling på såväl server-sidan som klient-sidan.

En viktig nyhet i ES var ett genomtänkt modulsystem, för att definiera moduler med exporterade funktioner och sedan kunna importera dessa.

Då Node.js skapades fanns inget sådan, utan man inkorporerade Common.js, som hade ett system för hantering av moduler. Detta har fungerat mycket väl under alla år. Men, så kom då ES Modules (ESM) och ställde till det. Hur skulle man hantera detta och se till att all existerande kod fortfarande fungerade?

Föga förvånande dröjde det länge innan ESM blev normalt tillgänglig i Node.sj. Först med version 15, kan man säga att ESM var fullt ut tillgänglig i Node.js. Innan dessa fanns det implementerat i ett flertal versionen, men som ett s.k. experimentell tillägg, som t.o.m skrev ut en varning på terminalen!

Våran kurs om Node.js, har precis genomgått en omfattande renovering med många nya kapitel. Vi har bland annat förlängt kursen från två till tre dagar. En central del är att alla nyskrivna eller omarbetade programexempel nu använder ESM.

Låt oss kika på ett litet programexempel. Vi har en trivial liten fil med två funktioner, som vi vill exponera till andra filer/bibliotek/program.

function sum(n) {
    if (n <= 0) return 0;
    return n * (n+1) / 2;
}

function product(n) {
    if (n <= 0) return 0;
    if (n === 1) return 1;
    return n * product(n-1);
}

I klassisk Node.js, dvs med Common.js Modules, hade det sett ut på följande vis.

function sum(n) { ... }
function product(n) { ... }

module.exports = {
    sum: sum,
    product: product
}

Med modern Node.js, skriver vi istället export framför det som ska exporteras, d.v.s. vara anropsbart från en annan modul. Observer att det finns flera olika syntaktiska konstruktioner för export. Här visar jag bara den mest rättframma.

export function sum(n) { ... }
export function product(n) { ... }

För att sen inkorporera denna lilla modul i annan fil, skrev man i klassisk Node.js

const numfn = require('./numbers.js');

const N = 10;
console.log('sum(1..%d)  = %d', N, numfn.sum(N));
console.log('prod(1..%d) = %d', N, numfn.product(N));

Medan man nu i modern Node.js (och givetvis modern JS) skriver

import { sum, product } from './numbers.js';

const N = 10;
console.log('sum(1..%d)  = %d', N, sum(N));
console.log('prod(1..%d) = %d', N, product(N));

För att de två modulsystemen ska kunna samexistera, så måste man ange till JS engine vilken av de två man vill använda.

Om man har ett mixat system, ska ESM filer ha filändelsen *.mjs. Det rekommenderas också att för övriga filer uppdatera filändelsen til *.cjs, för att vara konsekvent.

För ett nytt projekt, blir det mycket enklare. Lägg in följande i package.json

"type": "module"

samt, använd bara ESM i alla *.js filer. Det är precis så vi gör i alla nya och omskrivna program-exempel.

Här följer till sist, kursen uppdaterade innehåll:

Background and Overview

  • History
  • Ryan Dahl
  • Properties
  • Architecture
  • Wrapping the LINUX system API
  • Installation of NodeJS and NPM
  • Running the REPL
  • Running script files
  • NodeJS API documentation

1 - MODULES

Common.js Modules

  • Modules in Node.js
  • Understanding require()
  • Usage of built-in (core) modules
  • User-defined file modules
  • User-defined folder modules
  • Modules folder
  • Understanding the module concept
  • Scope
  • IIFE
  • The module design pattern
  • How to implement a simple require()
  • A glims beind Node's own implementation of require()

ES Modules (ESM)

  • ES versions and Node.js
  • What is ES modules?
  • ESM support in Node and from which version
  • How to apply ESM in modern Node
  • Export forms
  • Import forms
  • Path forms
  • Importing core API modules

Using TypeScript with Node.js

  • Anders Hejlsberg
  • Brief about strong typing
  • Installation of TS

2 - REMOTE MODULES

Using NPM

  • Isaac Z Schluter
  • What is NPM?
  • Installation of a NPM local package
  • Installation of a remote NPM package
  • Dependencies, app vs. dev
  • Useful commands
  • Beware of the node-bloat problem
  • Publishing a NPM package
  • Using a private NPM registry
  • Verdaccio
  • Alternatives to NPM, such as PNPM and Yarn

Node Scripts

  • Node shell scripts
  • Deployment
  • Installation

Using NPX

  • The problem of globally installed Node scripts
  • Install locally and use NPX
  • How to install ephemerally and use NPX
  • Usage of the package option
  • NPX is really NPM exec
  • Kick-starting a fresh project
  • Script entries in package.json

3 - ASYNCHRONOUS EXECUTION

Call-Backs

  • How do JS execute?
  • The JS event loop
  • The JS call-back queue
  • The event loop of Node
  • Postponed and periodic computation
  • The Node API is a wrapper around the Linux API
  • Why you cannot translate a C program straight into Node
  • Understanding the continuation style

Promises

  • Limitations of the call-back model of design
  • Understanding a JS promise
  • Using promises
  • Promise chain
  • Usage of .then() and .catch()
  • How to convert a function taking a call-back into a function returning a promise

Async Functions

  • Similarity between a promise-chain and a try-catch block
  • Async functions in ES2017
  • Using async and await
  • Async lambda
  • Using await in a loop
  • Top-level await in ES2020

4 - EMITTERS AND STREAMS

Event Emitters

  • What is an event emitter in Node?
  • Event types and listeners
  • Emitter definition
  • Simple emitter
  • Receive events
  • Receive an event just once
  • Dealing with event errors

Buffers

  • What is a Node buffer?
  • Allocating a buffer
  • Usage of a buffer
  • Slicing a buffer
  • Encodings

Streams

  • What is a Node stream?
  • Using .pipe()
  • Stream types
  • Implementing a Readable
  • Implementing a Writable
  • Implementing a stream transformer
  • Assembly of stream pipeline
  • The LineTransformer
  • The SplitTransformer
  • The Array2ObjectTransformer
  • The JsonArrayTransformer
  • Useful helper libraries

5 - CORE API

Working with Processes

  • Child processes in Node
  • Using execFile()
  • Using exec()
  • Using spawn()
  • Using fork() and send messages
  • Handling POSIX signals
  • Sending signals

Working with Files

  • The fs module
  • List of functions with Linux similarities
  • Promised based functions
  • The path module
  • POSIX error codes
  • Different ways writing to stdout

HTTP Server

  • List of built-in network services
  • A simple HTTP server
  • Serving files
  • A simple REST WS server

HTTP Client

  • Simple GET request
  • Handling errors
  • Simple POST request, sending data

6 - SELECTED FRAMEWORKS

Unit Testing with Jest

  • Installation and configuration
  • Jest and ESM and how to make it work
  • Writing a test function
  • Test suite execution
  • Understanding matchers
  • User-defined matcher
  • Testing exceptions
  • Testing async code
  • Test code coverage
  • Life-cycle functions

HTTP Client with Fetch

  • Why use a 3rd party lib for HTTP client access?
  • Installation and configuration of node-fetch
  • Simple GET request
  • Performing non-GET requests
  • Sending data
  • Simple CRUD REST sequence
  • Using json-server

HTTP Server with Express

  • Why use a 3rd party lib for HTTP server provisioning
  • Installation and configuration
  • Simple Express app
  • Understanding app components
  • Defining routes
  • Defining filters
  • Defining middlewares
  • Simple HTML/CSS/JS based web app using Express
  • Template engines
  • Implementation of a simple template engine
  • Using the Express generator
  • Simple REST WS server with CRUD operations

Using a MySQL Database

  • Installation och configuration of mysql2
  • Connecting to a MySQL server
  • How to create and drop tables
  • How to perform basic CRUD operations
  • An Express app using Handlebars template and engine and a MySQL database
  • Brief about installing MySQL
  • Installing and using MySQL using a Docker container

7 - TOOLS

Transpilers

  • What is a transpiler?
  • JavaScript transpilers
  • Installation and usage of Babel
  • HTML transpilers
  • Usage of Pug
  • CSS transpilers
  • Usage of SCSS
  • Post-processors
  • Minification

Builders

  • What is a build tool?
  • Using Gulp

Bundlers

  • What is a bundler?
  • Typical operations of a bundler
  • Popular bundlers
  • Using Parcel
  • Using Vite

Länkar