Serverless Functions: Das Geheimnis für ultra-produktive Front-End Teams

Avatar of Jason Lengstorf
Jason Lengstorf am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

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.

  1. Committe die Serverless Function und netlify.toml in das Repository und pushe sie auf GitHub, Bitbucket oder GitLab.
  2. 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.