Különféle webes projektjeink során gyakran használunk Node.js-t, elsősorban nagy terhelésre tervezett microservicek, valamint CLI eszközök és ütemezett feladatok futtatására. Legfontosabb előnye számunkra, hogy más megoldásokhoz (pl. Ruby on Rails) képest jobban optimalizált, a leggyakoribb feladatokra pedig 3rd party programkönyvtárak széles tárháza áll a fejlesztők rendelkezésére.

Az előnyök mellett azonban több olyan nehézséggel is rendszeresen találkozunk, amelyek megnehezítik a fejlesztést: a beépített Typescript támogatás hiánya, az elavult, callback alapú API, valamint a natív addonok fejlesztésének körülményessége, melyre különösen a legacy C/C++ projektekkel történő együttműködés során lenne szükség.

Érdeklődve szemléltük tehát Ryan Dahl, a Node.js ötletgazdájának előadását, melyben személyes véleményét osztotta meg a közönséggel a Node.js jelenlegi állapotáról, valamint bejelentette a Deno fejlesztését, amellyel a legfontosabb hiányosságokat kívánja orvosolni. Mostanra hihetetlen módon felkapta a fejlesztői közösség a Deno-t, néhány nappal az 1.0-ás verzió megjelenése után a hivatalos GitHub repó már negyvenezer csillagozás fölött jár (összehasonlításként a Node nemrég lépte át a hetvenezret).

Legfőbb eltérések a Node-hoz képest

TLDW: Ryan az előadásában legfőként az alábbi hátrányokat emelte ki:

  • Callback alapú API, amely az async/await terjedésével egyre esetlenebbé teszi a beépített Node API-k használatát.
  • Biztonság, azaz a jogosultságkezelés teljes hiánya – egy Node.js alkalmazás (és vele együtt a használt 3rd party libraryk is) gyakorlatilag bármit megtehet, amihez a futtató felhaszálónak jogosultsága van.
  • package.json használata és az npm integrálása.
  • node_modules, avagy a legnehezebb objektum az univerzumban (megj.: bár különféle eszközökkel, mint pl. modclean, node-prune kordában tartható)
  • node-gyp, azaz a V8 eredeti build rendszere, amely gyakran okoz fejfájást a natív addonok fejlesztőinek.

A Deno főként a fentiekre kíván megoldást nyújtani, de ezen kívül más, kisebb eltéréseket is találhatunk. Lássuk, melyek ezek.

Első benyomások

Ami telepítés után rögtön feltűnik, hogy egyetlen, kb. 40 megás binárisként érkezik, amely portable alkalmazásoknál kifejezetten jól jöhet, de a Docker képfájlok létrehozását is egyszerűsítheti.

A Deno beépített TypeScript támogatással rendelkezik, így nincs szükség sem third party megoldások használatára (ts-node, babel), sem speciális konfigurációra. Emellett természetesen a natúr JavaScript is használható, vagy akár vegyíthető is a két megoldás egy alkalmazáson belül.

A külső függőségek importálása a böngészőkben megszokott módon, URL-ek segítségével történik. A gyakorlatban így akár egyetlen állományból, pl. egy külső CDN-en tárolt, minifikált JS modulból is behúzható egy függőség, amennyiben az megfelel az ES modulok követelményeinek. A modulokat nem szükséges külön telepíteni, azok az app első futtatásakor automatikusan letöltődnek, és egy központi tárolóba kerülnek. Ennek pontos helyét a deno info parancs segítségével nézhetjük meg:

$ deno info
DENO_DIR location: "/Users/tamas/Library/Caches/deno"
Remote modules cache: "/Users/tamas/Library/Caches/deno/deps"
TypeScript compiler cache: "/Users/tamas/Library/Caches/deno/gen"

Ezzel jelentős helyet takaríthatunk meg a Node.js megoldásához képest, egyrészt mert a több projektekben is használt függőségek csak egyszer foglalnak helyet, másrészt mert itt ténylegesen csak a szükséges JS fájlok kerülnek letöltésre – szemben a node-dal, ahol az egyes modulok rengeteg "ott felejtett" állományt is tartalmaznak. A gyakorlatban jelenleg sajnos problémát jelent a már nem használt modulok törlése, erre később várható hivatalos megoldás.

Mivel az egyes modulok bárhonnan letölthetőek, nincs szükség sem központi package manager-re, sem központi package repository-ra. A 3rd party modulok könnyebb megtalálhatósága érdekében ugyan a Deno hivatalos oldalán üzemel egy repository, ez inkább egy linkgyűjtemény, használata nem kötelező. A szabadságnak azonban ára van: míg egy központi repóból viszonylag ritkán törlődnek modulok (ha mégis, az kellemetlen napokat okozhat a fejlesztőknek, lásd a left-pad esetét), addig egy random weboldalon tárolt függőség bármikor nyom nélkül eltűnhet, vagy lecserélődhet egy kártékony kódot tartalmazó változatra. A későbbiekben production alkalmazások esetén különösen fontos lesz a megbízható CDN-ek, vagy akár belső mirrorok használata.

Rögtön a Deno standard library kipróbálása során feltűnik a jogosultságkezelés jelenléte: a potenciálisan veszélyes műveleteket, mint pl. internet hozzáférés, fájl I/O, vagy a környezeti változók olvasása külön engedélyezni kell a futtatandó alkalmazás számára a megfelelő kapcsolókkal (--allow-net, --allow-env, --allow-read, stb). Manapság egyre több támadásról hallani népszerű modulok ellen, így ez mindenképp hasznos adalék a fejlesztői környezet védelme érdekében. Éles rendszerek esetében is csökkenti egy esetleges támadás hatását, de önmagában nem helyettesíti a külső függőségek auditálását, valamint az egyes alkalmazások megfelelő izolációját.

Az IDE támogatás terén is meglepően jó a helyzet: több népszerű plugin-t is találtunk a Visual Studio Code-hoz, melyek a néhány napos kísérletezés során többnyire megfelelően működtek.

Docker build és használat éles környezetben

A single binary megoldásnak, valamint a package manager hiányának köszönhetően az alkalmazások dockerizálása meglepően egyszerű. Habár hivatalos Docker image egyelőre még nem készült, a közösség több projektet is fenntart a hiány pótlására. A tesztelés során a választásunk a hayd/deno-docker projektre esett, azon belül pedig az Alpine Linux alapú képfájlra, annak kis mérete miatt.

Habár első ránézésre elegendőnek tűnhet csak az app forrását becsomagolni, a külső függőségek letöltését pedig futás közben a Deno-ra bízni, a gyakorlatban mégis célszerű a függőségeket már a build során letölteni és a képfájlba csomagolni. Ennek számos előnye van:

  • Az esetleges problémák, szinkatikai hibák már a build során kiderülnek.
  • Stabil docker képfájlt kapunk, azaz később elindítva is garantáltan ugyan azokat a verziókat használja.
  • Lehetőségünk van a letöltött modulok átvizsgálására automatizált eszközökkel.
  • Az alkalmazás indítása és újraindítása gyorsabbá válik.
  • Kevésbé lesz érzékeny az átmeneti hálózati problémákra, ha azok az alkalmazás indulása vagy újraindulása során jelentkeznének.

A függőségek letöltésére a deno cache parancs szolgál, amelyet a képfájl építése során érdemes meghívni.

Szintén hasonló célokat szolgálna a deno bundle parancs is, amely egyetlen .js fájlba tömörítené a teljes alkalmazást, azonban ennek használata során stabilitási problémákba ütköztünk. Remélhetőleg egy későbbi Deno verzióban javításra kerül.

Hasznos lehet továbbá a --quiet (-q) opció használata, amellyel a diagnosztikai üzenetek kapcsolhatók ki, így azok nem vegyülnek az alkalmazás kimenetével.

A fentiek alapján így néz ki egy minta Dockerfile:

FROM hayd/alpine-deno:1.0.2

# Create app directory
WORKDIR /app

# Prefer not to run as root.
USER deno

# Bundle app source
COPY . .

# Compile the main app so that it doesn't need to be compiled on each startup.
RUN deno cache src/main.ts

EXPOSE 3000

ENTRYPOINT deno run --allow-net --allow-hrtime --allow-env --allow-read --quiet src/main.ts

CMD []

Egyéb felhasználási lehetőségek

A szerver oldali fejlesztésen kívül további érdekes felhasználási módokra is lehetőség nyílhat a jövőben. Számunkra ezek közül a legígéretesebbnek az így készült alkalmazások egyetlen futtatható állományba történő becsomagolása tűnik, mely a fejlesztők hosszú távú céljai között szerepel. A Node.js-hez már most is rendelkezésre állnak hasonló megoldások, például a warp, az nexe, vagy az Electron alkalmazások becsomagolásához gyakran használt ASAR technológia, ezek azonban sajnos nehezen birkóznak meg a natív addonokkal, valamint az eredeti forráskód is viszonylag könnyen kibontható az így készült futtatható állományból. A Deno fejlesztői által tervezett megoldás előnye, hogy V8 JavaScript motor Snapshot-jaira építve egy bináris, "félig lefordított" változat kerülne beépítésbe az elkészült állományba, amely így gyorsabban indul, és a visszafejtése is sokkal nehezebb. Reményeink szerint ez az irány a jövőben lehetővé teszi majd, hogy JS/TS környezetben is fejleszthetőek legyenek kis méretű, függőségektől mentes, egyetlen állományból álló CLI szoftverek, esetleg a későbbiekben asztali alkalmazások.

Hátrányok, hiányosságok

Mint minden új technológiának, így a Deno-nak is megvannak a maga gyermekbetegségei. A tesztelés során, elvétve ugyan, de találkoztunk különféle kompatibilitási problémákkal és kiforratlan hibaüzenetekkel.

A Node.js-ből ismert, népszerű modulok jelentős része még nem érhető el, az alternatívák pedig gyakran nem alkalmasak éles környezetben történő használatra. Ugyan a Deno támogatja az ES modulokat, a Node-ból ismert require hívást viszont nem, ahogy a standard library sem kompatibilis a Node.js-sel. Szerencsére számos fejlesztő dolgozik azon, hogy egyre több modul legyen elérhető Deno alatt is, így idővel ez változni fog.

Különösen nagy nehézséget jelent a megváltozott környezet a natív addonokra épülő modulok esetén. Jó példa erre az SQLite modul, amely jelenleg WebAssembly-n keresztül érhető el, teljesítménye és rugalmassága jócskán alulmarad a Node-ból megszokott megoldásoknak.

Habár kezdetben nem hiányzott, a néhány napos tesztelés során egyre szembetűnőbbé vált a package.json hiánya, amelyben egy helyen megtalálható az alkalmazás összes külső függősége. Ez segíti a külső modulok auditálását, valamint az alkalmazás különböző részei által importált verziók szinkronban tartását. Alternatívaként a Deno csapata egy központi deps.ts fájl használatát javasolja, amely valóban megoldást jelent a problémák egy részére, azonban várhatóan nehezebb lesz automatizált eszközökkel feldolgozni a Node hasonló megoldásánál.

Összegzés

A felsorolt hátrányok ellenére összességében elnyerte csapatunk tetszését a Deno, amely a jövőben a Node.js alternatívája lehet a webalkalmazások fejlesztése során. Az ökoszisztéma fejletlensége és a tapasztalt stabilitási problémák miatt éles használatra még nem ajánljuk, de mindenképp figyelemmel fogjuk kísérni a technológia fejlődését. A többi már a fejlesztőközösségen múlik :)