Moderne Apps stellen hohe Anforderungen an Front-End-Entwickler. Web-Apps erfordern komplexe Funktionalität, und der Löwenanteil dieser Arbeit fällt den Front-End-Entwicklern zu
- Erstellung moderner, barrierefreier Benutzeroberflächen
- Erstellung interaktiver Elemente und komplexer Animationen
- Verwaltung komplexer Anwendungszustände
- Meta-Programmierung: Erstellen von Skripten, Transpilern, Bundlern, Lintern usw.
- Lesen von REST-, GraphQL- und anderen APIs
- Middle-Tier-Programmierung: Proxys, Weiterleitungen, Routing, Middleware, Authentifizierung usw.
Diese Liste allein ist schon einschüchternd, aber sie wird richtig schwierig, wenn Ihr Tech-Stack nicht auf Einfachheit ausgelegt ist. Eine komplexe Infrastruktur führt zu versteckten Verantwortlichkeiten, die Risiken, Verlangsamungen und Frustration mit sich bringen.
Je nach gewählter Infrastruktur können wir einem Front-End-Entwickler unbeabsichtigt auch Serverkonfiguration, Release-Management und andere DevOps-Aufgaben aufbürden.
Softwarearchitektur hat direkten Einfluss auf die Teamproduktivität. Wählen Sie Werkzeuge, die versteckte Komplexität vermeiden, um Ihre Teams zu befähigen, mehr zu erreichen und sich weniger überlastet zu fühlen.
Die heimtückische Mittelschicht – wo Front-End-Aufgaben stark an Komplexität zunehmen können
Betrachten wir eine Aufgabe, die ich mehreren Front-End-Teams zugewiesen gesehen habe: Erstellen einer einfachen REST-API, um Daten aus mehreren Diensten in einer einzigen Anfrage für das Frontend zu kombinieren. Wenn Sie gerade Ihren Computer angeschrien haben: „Aber das ist keine Frontend-Aufgabe!“ – stimme ich zu! Aber wer bin ich, Fakten das Backlog behindern zu lassen?
Eine API, die nur vom Frontend benötigt wird, fällt unter die Kategorie Middle-Tier-Programmierung. Wenn zum Beispiel das Frontend Daten von mehreren Backend-Diensten kombiniert und einige zusätzliche Felder ableitet, ist ein gängiger Ansatz, eine Proxy-API hinzuzufügen, damit das Frontend nicht mehrere API-Aufrufe tätigt und eine Menge Geschäftslogik auf der Client-Seite ausführt.
Es gibt keine klare Grenze, welches Backend-Team eine solche API besitzen sollte. Sie auf die Liste eines anderen Teams zu setzen – und zukünftige Updates zu erhalten – kann ein bürokratischer Albtraum sein, sodass das Frontend-Team die Verantwortung übernimmt.
Dies ist eine Geschichte, die je nach unseren architektonischen Entscheidungen unterschiedlich endet. Schauen wir uns zwei gängige Ansätze für die Bewältigung dieser Aufgabe an
- Erstellen einer Express-App auf Node, um die REST-API zu erstellen
- Verwenden von Serverless Functions, um die REST-API zu erstellen
Express + Node bringt eine überraschende Menge an versteckter Komplexität und Overhead mit sich. Serverless ermöglicht es Front-End-Entwicklern, die API schnell bereitzustellen und zu skalieren, damit sie sich wieder ihren anderen Front-End-Aufgaben widmen können.
Lösung 1: API mit Node und Express (und Docker und Kubernetes) erstellen und bereitstellen
Früher in meiner Karriere war die Standardvorgehensweise, Node und Express zu verwenden, um eine REST-API zu erstellen. Oberflächlich betrachtet scheint dies relativ einfach zu sein. Wir können die gesamte REST-API in einer Datei namens server.js erstellen.
const express = require('express');
const PORT = 8080;
const HOST = '0.0.0.0';
const app = express();
app.use(express.static('site'));
// simple REST API to load movies by slug
const movies = require('./data.json');
app.get('/api/movies/:slug', (req, res) => {
const { slug } = req.params;
const movie = movies.find((m) => m.slug === slug);
res.json(movie);
});
app.listen(PORT, HOST, () => {
console.log(`app running on http://${HOST}:${PORT}`);
});
Dieser Code ist nicht *zu weit* von Front-End-JavaScript entfernt. Es gibt eine anständige Menge an Boilerplate-Code, der einen Front-End-Entwickler stolpern lässt, wenn er ihn noch nie gesehen hat, aber er ist beherrschbar.
Wenn wir node server.js ausführen, können wir https://:8080/api/movies/some-movie besuchen und ein JSON-Objekt mit Details zu dem Film mit dem Slug some-movie sehen (vorausgesetzt, Sie haben diesen in data.json definiert).
Deployment führt zu einer Menge zusätzlichem Overhead
Das Erstellen der API ist jedoch erst der Anfang. Wir müssen diese API so bereitstellen, dass sie eine ordentliche Menge an Traffic bewältigen kann, ohne zusammenzubrechen. Plötzlich wird die Sache viel komplizierter.
Wir brauchen mehrere zusätzliche Tools
- einen Ort zum Bereitstellen (z. B. DigitalOcean, Google Cloud Platform, AWS)
- einen Container, um lokale Entwicklung und Produktion konsistent zu halten (d. h. Docker)
- eine Möglichkeit, sicherzustellen, dass das Deployment live bleibt und Traffic-Spitzen bewältigen kann (d. h. Kubernetes)
An diesem Punkt sind wir weit außerhalb des Front-End-Bereichs. Ich habe solche Arbeiten schon gemacht, aber meine Lösung bestand darin, aus einem Tutorial oder einer Stack-Overflow-Antwort zu kopieren und einzufügen.
Die Docker-Konfiguration ist einigermaßen verständlich, aber ich habe keine Ahnung, ob sie sicher oder optimiert ist.
FROM node:14
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 8080
CMD [ "node", "server.js" ]
Als Nächstes müssen wir herausfinden, wie wir den Docker-Container in Kubernetes bereitstellen. Warum? Ich bin mir nicht wirklich sicher, aber das ist es, was die Backend-Teams im Unternehmen verwenden, also sollten wir Best Practices befolgen.
Dies erfordert mehr Konfiguration (alles kopiert und eingefügt). Wir vertrauen unser Schicksal Google an und kommen zu Dockers Anleitungen zum Bereitstellen eines Containers auf Kubernetes.
Unsere ursprüngliche Aufgabe „schnelle Node-API aufsetzen“ hat sich zu einer Suite von Aufgaben ausgewachsen, die nicht zu unserem Kernkompetenzbereich passen. Als mir zum ersten Mal eine solche Aufgabe übertragen wurde, habe ich mehrere Tage damit verbracht, alles zu konfigurieren und auf Feedback von den Backend-Teams zu warten, um sicherzustellen, dass ich nicht mehr Probleme verursache, als ich löse.
Einige Unternehmen haben ein DevOps-Team, das diese Arbeit überprüft und sicherstellt, dass sie nichts Schlimmes tut. Andere vertrauen auf den Hivemind von Stack Overflow und hoffen das Beste.
Bei diesem Ansatz beginnen die Dinge überschaubar mit etwas Node-Code, eskalieren aber schnell in mehrere Konfigurationsebenen, die Fachgebiete abdecken, die weit über das hinausgehen, was wir von einem Frontend-Entwickler erwarten sollten.
Lösung 2: Dieselbe REST-API mit Serverless Functions erstellen
Wenn wir uns für Serverless Functions entscheiden, kann die Geschichte dramatisch anders aussehen. Serverless ist ein großartiger Begleiter für Jamstack-Webanwendungen, der Front-End-Entwicklern die Möglichkeit gibt, Middle-Tier-Programmierung ohne die unnötige Komplexität der Bereitstellung und Skalierung eines Servers zu bewältigen.
Es gibt mehrere Frameworks und Plattformen, die die Bereitstellung von Serverless Functions schmerzfrei machen. Meine bevorzugte Lösung ist die Verwendung von Netlify, da es eine automatisierte Continuous Delivery sowohl für das Frontend als auch für Serverless Functions ermöglicht. Für dieses Beispiel werden wir Netlify Functions verwenden, um unsere Serverless-API zu verwalten.
Functions as a Service (eine schicke Art, Plattformen zu beschreiben, die Infrastruktur und Skalierung für Serverless Functions übernehmen) zu verwenden bedeutet, dass wir uns *nur* auf die Geschäftslogik konzentrieren können und wissen, dass unser Middle-Tier-Dienst riesige Mengen an Traffic bewältigen kann, ohne zusammenzubrechen. Wir müssen uns nicht mit Docker-Containern, Kubernetes oder gar dem Boilerplate-Code eines Node-Servers befassen – es funktioniert einfach™, sodass wir eine Lösung ausliefern und mit der nächsten Aufgabe fortfahren können.
Zuerst können wir unsere REST-API in einer Serverless Function unter netlify/functions/movie-by-slug.js definieren.
const movies = require('./data.json');
exports.handler = async (event) => {
const slug = event.path.replace('/api/movies/', '');
const movie = movies.find((m) => m.slug === slug);
return {
statusCode: 200,
body: JSON.stringify(movie),
};
};
Um das richtige Routing hinzuzufügen, können wir eine netlify.toml im Stammverzeichnis des Projekts erstellen.
[[redirects]]
from = "/api/movies/*"
to = "/.netlify/functions/movie-by-slug"
status = 200
Dies ist deutlich weniger Konfiguration, als wir für den Node/Express-Ansatz benötigen würden. Was ich an diesem Ansatz bevorzuge, ist, dass die Konfiguration hier auf das reduziert ist, was uns interessiert: die spezifischen Pfade, die unsere API verarbeiten soll. Der Rest – Build-Befehle, Ports usw. – wird mit guten Standardeinstellungen für uns behandelt.
Wenn wir die Netlify CLI installiert haben, können wir dies sofort lokal mit dem Befehl ntl dev ausführen, der Serverless Functions im Verzeichnis netlify/functions sucht.
Wenn wir https://:888/api/movies/booper besuchen, wird ein JSON-Objekt mit Details zum Film „booper“ angezeigt.
Bisher fühlt sich dies nicht *zu* anders an als die Node- und Express-Einrichtung. Wenn wir jedoch mit dem Deployment beginnen, ist der Unterschied *riesig*. Hier ist, was nötig ist, um diese Seite in Produktion zu stellen.
- Committe die Serverless Function und
netlify.tomlin das Repository und pushe sie auf GitHub, Bitbucket oder GitLab. - Verwende die Netlify CLI, um eine neue Site zu erstellen, die mit deinem Git-Repository verbunden ist:
ntl init.
Das war's! Die API ist jetzt bereitgestellt und kann bei Bedarf auf Millionen von Aufrufen skaliert werden. Änderungen werden automatisch bereitgestellt, sobald sie in den Haupt-Repository-Branch gepusht werden.
Sie können dies in Aktion unter https://serverless-rest-api.netlify.app sehen und den Quellcode auf GitHub überprüfen.
Serverless erschließt ein enormes Potenzial für Front-End-Entwickler
Serverless Functions sind kein Ersatz für alle Backends, aber sie sind eine äußerst leistungsfähige Option für die Middle-Tier-Entwicklung. Serverless vermeidet die unbeabsichtigte Komplexität, die zu organisatorischen Engpässen und erheblichen Effizienzproblemen führen kann.
Die Verwendung von Serverless Functions ermöglicht es Front-End-Entwicklern, Middle-Tier-Programmierungaufgaben zu erledigen, ohne den zusätzlichen Boilerplate- und DevOps-Overhead auf sich zu nehmen, der Risiken birgt und die Produktivität verringert.
Wenn unser Ziel darin besteht, Front-End-Teams zu befähigen, schnell und souverän Software auszuliefern, dann backt die Wahl von Serverless Functions Produktivität in die Infrastruktur ein. Seit ich diesen Ansatz als meinen Standard-Jamstack-Starter übernommen habe, kann ich schneller als je zuvor ausliefern, egal ob ich allein arbeite, mit anderen Front-End-Entwicklern oder teamübergreifend in einem Unternehmen.
Danke für diesen tollen Artikel!
Ihr Artikel ist wirklich interessant (und Sie haben meinen aktuellen lebenden Albtraum genau beschrieben...).
Ich habe mich gefragt, ob Sie weitere Ressourcen oder einen guten Tutorial-Link haben, um mehr über diese Serverless Functions zu erfahren und wie man sie erstellt/verwendet.
Lustigerweise habe ich in meiner Erfahrung gesehen, dass Serverless Functions zu mehr Arbeit führen, da Logging und Debugging anders sind und nicht bereits eingerichtet sind, wenn das Team bereits eine Menge Docker-basierter Dienste hat. Da Sie immer noch einen Dienst erstellen, sollten Sie bei dem bleiben, was das Team bereits tut, da es wahrscheinlich bereits ein gutes Muster dafür hat, wie es Dinge wie die Überwachung von Fehlern, das Logging usw. handhabt...
Ich war tatsächlich gegen Serverless, aber nach diesem Artikel bin ich bekehrt.
Ich habe mit Serverless in der Produktion gearbeitet und dies trivialisiert einige reale Overhead-Probleme mit Serverless, die oberflächlich behandelt wurden.
Es fördert seltsame Designmuster, mit Connection-Pooling, und das Setzen einfacher Cache-Header wird wirklich schwierig.
Anfragen dürfen nicht länger als 30 Sekunden dauern, was bedeutet, dass alles, was lange läuft, fehlschlägt.
Wenn Sie sich in einer VPC befinden, gibt es 8 Sekunden Latenz pro Kaltstart.
Web API Gateway ist furchtbar.
Geringe Beobachtbarkeit ohne erheblichen Vorabaufwand für die lokale Entwicklung.
Keine einfache Möglichkeit, SNS oder SQS lokal einfach zu mocken.
Es ist überhaupt keine goldene Lösung, und wenn Sie einen PaaS-Dienst hätten, kann die Arbeit mit einem Container viel einfacher sein!
Function as a Service wird aufgrund seiner höheren Effizienz und geringeren Infrastrukturkosten immer beliebter. Cloud Networking ist eines der glänzenden Beispiele für FaaS-Dienste. Danke für das Teilen wertvoller Inhalte.
Ich habe gesehen, dass Serverless neben Server-Docker-Kubernetes-Architekturen eingesetzt wird, und es führt ein sehr entscheidender Fehler in die lokale Entwicklung ein – stellen Sie sich vor, Sie führen den Cluster lokal (localhost) aus, während ein Teil Ihres Systems nur auf Dev/Staging/Produktion laufen kann, z. B. nur mit nicht-lokalen Datenbanken und APIs „sprechen“ kann. Dies ist nachteilig für Entwicklung und Tests.