Diese Woche beschäftigen wir uns mit Bun, welches im September 2023 in der Version 1.0 released wurde. Die Entwickler beschreiben Bun als ein All-in-one Toolkit für JavaScript und TypeScript Anwendungen.
Die Bun Runtime
Im Herzen der Bun-Runtime wurde eine schnelle JavaScript-Runtime implementiert, welche dabei als problemlose Alternative zu Node.js konzipiert wurde. Sie ist in Zig geschrieben und wird von JavaScriptCore unter der Haube angetrieben, was die Startzeiten und den Speicherverbrauch dramatisch reduziert. Zig ist eine moderne, leistungsstarke Programmiersprache die durch die Kombination aus Stabilität, Effizienz und direkter Kontrolle über Ressourcen ausgewählt wurde.
Das Bun CLI-Tool
Das Command-Line-Tool von Bun ist zusätzlich mit einem Test-Runner, Script Runner und Node.js kompatiblem Paketmanager ausgestattet. Der grosse Unterschied dabei ist die deutlich bessere Performance im Vergleich zu bestehenden Tools. Ein weiterer grosser Vorteil liegt darin, dass bun ohne grössere Änderung in bestehenden Node.js-Anwendungen verwendet werden kann.
Schnellere Developer Experience als Node.js
Seit der Einführung von Node.js vor etwa 14 Jahren kamen immer wieder unzählige neue Features und Tools dazu, wodurch Node.js natürlich extrem wuchs. Dies führte zu einer gewissen Komplexität und Grösse, die zu Einbussen bei der Performance führte. Die vorhandenen Tools sind in den meisten Fällen sehr gut, da jedoch alle auf einmal vorhanden sind, führt dies zu einer langsamen Developer-Experience.
Dies hat vor allem den Grund, dass die vorhanden Tools oft Redundanzen in ihren Aufgaben führen. Führt man beispielsweise jest
aus, wird der Code mindestens dreimal geparsed. Dies ist genau die Stelle an der bun ansetzen und auch durch seine Geschwindigkeit überzeugen möchte, ohne dabei die Vorzüge von JavaScript zu verlieren.
Bun’s JavaScript Runtime
Bei der Einwicklung wurde Wert vor allem auf Geschwindigkeit gelegt. Vor allem bei TypeScript-Files ist alleine der Startprozess von Bun 4x so schnell wie von Node.js.
Bild Quelle: bun.sh
Wieso ist Bun so viel schneller?
Dieser Unterschied ist vor allem Möglich, da hier nicht Google’s V8 Engine benutzt wird, sondern Apples WebKit Engine.
Doch auch die Unterstützung von Typescript und JSX-Dateien war wichtig. Dies wird so gelöst, dass Buns Transpiler diese Dateien vor der Ausführung in herkömmliches JavaScript umwandelt, ohne zusätzliche Dependencies:
|
|
Weiterer Support ist natürlich auch für ES-Modules gegeben und empfohlen. Sollte dennoch wie bei Millionen von Packages auf npm CommonJS benötigt werden, wird auch dies unterstützt.
Bun unterstützt import
und require()
in derselben Datei
In Bun besteht die bemerkenswerte Möglichkeit, sowohl import
als auch require()
innerhalb derselben Datei zu nutzen – eine Flexibilität, die in Node.js standardmäßig nicht gegeben ist, es sei denn, man verwendet spezielle Funktionen wie das “Mixed-Modules”-Feature.
Standard Web-API-Unterstützung
Bun implementiert Standard-Web-APIs wie fetch
, Request
, Response
, WebSocket
, and ReadableStream
. Bun wird vom JavaScriptCore-Engine angetrieben, die von Apple für Safari entwickelt wurde. Daher verwenden einige APIs wie Headers
und URL
direkt die Implementierung von Safari.
Vollständige Kompatibilität mit Node.js Globals und Modules
Natürlich ist das Ziel von Bun die vollständige Kompatibilität mit den Node.js integrierten globals
(process
, Buffer
) und modules (path
, fs
, http
, usw
.). Hier ist jedoch zu erwähnen dass dies ist ein laufenderProzess ist, der noch nicht vollständig abgeschlossen ist. Der aktuelle Stand kann hier geprüft werden: https://bun.sh/docs/runtime/nodejs-apis.
Ebenfalls soll laut Bun die Kompatibilität zu bestehenden Frameworks gegeben sein. Herzu gehören:
- Next.js
- Remix
- Nuxt
- Astro
- SvelteKit
- Nest
- SolidStart
- Vite
Hot-Reloading mit Bun
Bun vereinfacht die Entwicklungsarbeit erheblich. Du kannst Bun mit dem Parameter --hot
ausführen, um das Hot-Reloading zu aktivieren, das deine Anwendung neu lädt, wenn Dateien geändert werden.
|
|
Im Gegensatz zu Werkzeugen wie nodemon
, die den gesamten Prozess hart neu starten, lädt Bun deinen Code neu, ohne den alten Prozess zu beenden. Das bedeutet, dass HTTP- und WebSocket-Verbindungen nicht getrennt werden und der State nicht verloren geht. Dieser Ansatz stellt einen Vorteil gegenüber gängigen Node.js-Tools dar, die bei Änderungen im Code den gesamten Prozess neu starten und daher bestehende Verbindungen unterbrechen.
Extreme Flexibilität mit Bun Plugins
Bun ist darauf ausgerichtet, extrem anpassbar zu sein. Mit der Möglichkeit, Plugins zu definieren, kannst du Imports abfangen und individuelle Ladevorgänge durchführen. Ein Plugin kann beispielsweise die Unterstützung für weitere Dateitypen wie yaml
oder png
hinzufügen. Die Plugin-API ist von esbuild
inspiriert, was bedeutet, dass die meisten esbuild-Plugins problemlos mit Bun verwendet werden können. Dies verleiht dir maximale Flexibilität bei der Gestaltung deiner Entwicklungsprozesse.
|
|
Bun APIs
Bun bringt top optimierte APIs als Standardbibliothek mit, die genau das bieten, was du als Entwickler am meisten brauchst. Im Gegensatz zu den Node.js APIs, die eher für Rückwärtskompatibilität gedacht sind, sind diese nativen Bun-APIs darauf ausgerichtet, schnell und intuitiv benutzbar zu sein.
Bun.file()
Bun geht noch einen Schritt weiter, indem es ein BunFile
zurückgibt – eine Erweiterung des Web-Standard Files. Diese Datei ermöglicht das bedarfsgesteuerte, lazy laden von Inhalten in verschiedenen Formaten, was natürlich die Tür zu vielfältigen Anwendungsmöglichkeiten öffnet.
|
|
Bun.write()
Mit Bun vereinfacht die vielseitige Bun.write()
-API das Schreiben verschiedenster Daten auf die Festplatte. Ob es sich um einen String, binäre Daten, Blobs oder sogar ein Response-Objekt handelt – diese einzelnen Methoden optimieren den Schreibvorgang. Diese Flexibilität verbessert die Entwicklererfahrung erheblich, da es nahtlos möglich ist, unterschiedliche Datentypen zu verarbeiten, ohne auf mehrere komplexe Funktionen zurückgreifen zu müssen.
|
|
Bun.serve()
Bun bietet mit Bun.serve()
die Möglichkeit, einen HTTP-Server, WebSocket-Server oder beides zu starten. Dabei nutzt es vertraute Web-Standard-APIs wie Request
und Response
. Beeindruckend ist, dass Bun bis zu 4 mal mehr Anfragen pro Sekunde bedienen kann als Node.js. Diese Leistungsstärke in Kombination mit bekannten APIs macht Bun zu einer effizienten Wahl für das Bereitstellen von Serveranwendungen, wobei es gleichzeitig eine nahtlose Integration in gängige Webstandards ermöglicht.
|
|
Sogar TLS Konfigurationen können schnell und leicht implementiert werden:
|
|
Bun macht die Unterstützung von WebSockets neben HTTP mühelos. Definiere einfach einen Eventhandler innerhalb von websocket
. Dies steht im starken Kontrast zu Node.js, das keine integrierte WebSocket-API bereitstellt und auf externe Abhängigkeiten wie ws
angewiesen ist. Mit Bun wird die Implementierung von WebSockets in deine Anwendung so einfach wie nativer Code, ohne den zusätzlichen Aufwand externer Bibliotheken. Dazu kann Bun bis zu 5 mal mehr Anfragen pro Sekunde bedienen als Node.js.
|
|
bun:sqlite
Bun bietet integrierte Unterstützung für SQLite mit einer API, die sich von better-sqlite3
inspirieren lässt, aber in nativem Code geschrieben ist, um die Geschwindigkeit zu maximieren. Im Vergleich zu better-sqlite3
auf Node.js ermöglicht Bun Abfragen an SQLite mit bis zu 4-facher Geschwindigkeit. Diese nativ implementierte Unterstützung gewährleistet nicht nur eine schnelle und effiziente Datenbankinteraktion, sondern zeigt auch Buns Engagement für optimierte Leistung und nahtlose Integration von Datenbankfunktionalitäten in den Entwicklungsprozess.
|
|
Bun.password
Neben seinem vielseitigen Plugin-System erleichtert Bun auch die Umsetzung von gemeinsamen, jedoch komplexen Aufgaben, die Entwickler möglicherweise nicht von Grund auf selbst implementieren möchten.
Mit der Bun.password
-API können Entwickler mühelos Passwörter hashen und überprüfen, und das ganz ohne externe Abhängigkeiten. Diese Funktion ermöglicht die Verwendung anerkannter kryptografischer Algorithmen wie bcrypt oder argon2. Indem Bun diese einsatzbereiten Lösungen für essenzielle aber komplexe Funktionen bietet, vereinfacht es den Entwicklungsprozess und stärkt bewährte Sicherheitspraktiken. Entwickler können sich somit auf den Aufbau robuster und sicherer Anwendungen konzentrieren.
|
|
Der Package Manager
Auch wenn Bun nicht als primäre Laufzeitumgebung verwendet wird, kann der integrierte Paketmanager von Bun erheblich dazu beitragen, die Effizienz deines Entwicklungsworkflows zu steigern. Schluss mit den langen Wartezeiten beim Installieren von Abhängigkeiten, die du vielleicht von anderen Paketmanagern kennst.
Bun ist um ein Vielfaches schneller als npm
, yarn
und pnpm
. Dies wird durch die Nutzung eines globalen Modulcaches erreicht, um wiederholte Downloads aus dem npm-Registry zu vermeiden. Zudem nutzt es die schnellsten Systemaufrufe, die auf jedem Betriebssystem verfügbar sind.
Sieht man sich jedoch die Befehle an, erkennt man dabei eigentlich keinen Unterschied zu npm.
|
|
Auch beim Ausführen von Scripts mittels bun run
spart man jedes mal etwa 150 Millisekunden
Script Runner | ø Zeit |
---|---|
npm run |
176ms |
yarn run |
131ms |
pnpm run |
259ms |
bun run |
7ms |
Der Test Runner
Wenn du bereits Tests in JavaScript geschrieben hast, kennst du wahrscheinlich Jest, das die “expect
"-Style-APIs eingeführt hat.
Bun geht mit seinem integrierten Testmodul bun:test
einen Schritt weiter und ist vollständig kompatibel mit Jest. Du kannst deine Tests ganz einfach mit dem Befehl bun test
ausführen und profitierst dabei von allen Vorteilen der Bun-Laufzeitumgebung, einschließlich umfassender Unterstützung für TypeScript und JSX.
|
|
Die Migration von Jest oder Vitest zu Bun gestaltet sich spielend einfach. Jegliche Importe von @jest/globals
oder vitest werden automatisch auf bun:test
umgestellt, sodass alles reibungslos funktioniert, selbst ohne Codeänderungen.
|
|
In einem Leistungsvergleich mit dem Testpaket für zod hat Bun wieder die Nase vorn. Es war satte 13 mal schneller als Jest und 8-mal schneller als Vitest. Diese Performancesteigerung ist nicht nur eine Zahl; sie bedeutet effizienteres und schnelleres Testen für deine Projekte.
Zod ist eine leistungsstarke TypeScript-Bibliothek für die Definition von Datenstrukturen und deren Validierung, die es Entwicklern ermöglicht, klare und sichere Schemata für ihre Anwendungen zu erstellen.
Bild Quelle: bun.sh
Buns Matcher (bspw. expect().toEqual()
) sind durch die Implementierung nativem Code 100-mal schneller als in Jest und 10-mal schneller als in Vitest.
Es gibt auch direkt eine Implementierung für GitHub Actions:
|
|
Dadurch werden automatisch Annotation zu den Testfehlern hinzugefügt, damit deine Logs leicht verständlich sind.
Der Bundler
Bun hebt sich nicht nur als ein JavaScript- und TypeScript-Bundler sowie Minifier ab, sondern als eine kraftvolle Lösung, um deinen Code für den Browser, Node.js und andere Plattformen zu optimieren und zu bündeln. Inspiriert von esbuild bietet Bun eine durchdachte Plugin-API, die sowohl für das Bündeln als auch für die Laufzeitumgebung höchst effizient ist. Sogar das vorherige .yaml-Plugin ist weiter einsatzbereit, um .yaml-Dateien nahtlos während des Bündelns zu integrieren.
In puncto Geschwindigkeit hat Bun laut den Benchmarks von esbuild natürlich die Nase vorn. Es ist 1,75-mal schneller als esbuild selbst, 150-mal schneller als Parcel 2, 180-mal schneller als Rollup + Terser und beeindruckende 220-mal schneller als Webpack. Dank der integrierten Laufzeitumgebung und des Bundlers kann Bun Funktionen ausführen, die kein anderer Bundler beherrscht.
Bild Quelle: bun.sh
JavaScript Macros
Aber das ist noch nicht alles. Bun führt die Idee der JavaScript-Macros ein – das sind kleine Funktionen, die während des Bündelns deinen Code optimieren. Die Rückgabewerte dieser Funktionen werden direkt in dein Bundle eingefügt. Ein typischer Anwendungsfall wäre beispielsweise das Auslesen einer Release-Version. Hierfür benötigt es ein index.ts
File welches ein weiteres File release.ts
aufruft. Dies passiert sobald bun build index.ts
ausgeführt wird.
|
|
|
|
Hands-On Beispiele
Anschliessend möchte ich in einem kurzen Hands-On Beispiel zeigen, wie man mit der Entwicklung mittels Bun startet.
Installation
Ich habe mich dazu entschieden die Installation einfach über brew
durchzuführen. Es gibt jedoch auch die Möglichkeit dies über curl
, npm
, Docker
oder Proto
zu tun.
|
|
Projekt erstellen
Um direkt alle notwendigen Files zu haben und die wichtigsten Properties gesetzt zu haben, gibt es dieses Command:
|
|
Dadurch wird folgende Struktur bereitgestellt:
|
|
Server erstellen
Innerhalb des index.ts
-Files wird nun ein Endpunkt über den Port 3000 bereitgestellt, sobald bun run index.ts
ausgeführt wird. Dadurch wird durch den Aufruf von http://localhost:3000
der einfache Text "Hello b-nova"
bereits zurückgegeben.
|
|
Im nächsten Schritt möchten wir den Output etwas aufbereiten. Für dieses Beispiel benötigen wir die zwei Packages figlet
und @types/figlet
.
|
|
|
|
Sobald diese installiert sind, können diese importiert und wie folgt verwendet werden:
|
|
Dies erzeugt einen überarbeiteten Output:
Als letzten Schritt möchten wir nun verschiedene Router innerhalb unseres Servers anlegen.
|
|
Fazit
Insgesamt präsentiert sich Bun als beeindruckendes Toolkit für JavaScript und TypeScript, das mit innovativen Funktionen wie dem blitzschnellen Bundling, der Jest-Kompatibilität im Testen und der Performance-Steigerung durch native Implementationen glänzt.
Die durchdachte Integration von Laufzeitumgebung und Bundler sowie die Einführung von JavaScript-Makros machen Bun zu einer modernen Lösung, die Entwicklern ermöglicht, effizienten und performanten Code zu schreiben. Mit seiner Geschwindigkeit und Flexibilität bietet Bun eine vielversprechende Plattform für Entwickler, um in der sich ständig weiterentwickelnden JavaScript-Welt erfolgreich zu agieren. Aus unserer Sicht ein Must-Try! 🚀