Erstellung der elleXX-Website

  • Michelle Sanver

Close the gaps! Das ist der Leitspruch von elleXX, einer Website, die wir ins Leben gerufen haben, um genau das zu tun: FinanzlĂŒcken fĂŒr Frauen in der Schweiz schliessen. Wir haben diese Website mit Ghost CMS und einer massgeschneiderten API mit Nest.js erstellt.

Mehr zu unseren Custom Development und CMS services.

Aufbau von elleXX

elleXX ist eine Website, die sich an Frauen richtet, um sie bei der Schliessung von FinanzlĂŒcken zu unterstĂŒtzen. Sie wird von Journalistinnen betrieben, die viele nĂŒtzliche Inhalte zu diesem Thema verfassen. Als Frau in der Schweiz war es mir eine Ehre, an diesem Thema, das auch die Werte von Liip widerspiegelt, mitzuarbeiten. Das entscheidende Kriterium fĂŒr elleXX bestand darin, Inhalte schnell und einfach veröffentlichen zu können. Ausserdem gibt es einige Integrationen mit Produkten, welche die Leser*innen bei ihren finanziellen Entscheidungen unterstĂŒtzen. Wichtig war zudem, die Website-Member im Auge behalten zu können.

Beim Content Management haben wir uns fĂŒr Ghost CMS entschieden – und das aus gutem Grund. HauptsĂ€chlich, weil sich Ghost fĂŒr unser Hauptanliegen sehr gut eignet: Content. Der zweite Grund besteht darin, dass Ghost zu 100 % durch seine User*innen finanziert wird – es ist ein Open-Source-CMS!

Um Leads und Kontakte im Auge zu behalten, haben wir uns fĂŒr Friendly Automate entschieden. Das beste Argument hierfĂŒr: Es ist ein lokales Schweizer Unternehmen. Ausserdem basiert es auf Mautic, einem bekannten Framework fĂŒr Marketinglösungen.

Anschliessend brauchten wir einige kundenspezifische Integrationen mit Vontobel, MigrosBank und Cap Rechtsschutz. DafĂŒr haben wir mit dem JS-Framework Nest.js unsere eigene API entwickelt. Nest.js macht die Erstellung einer API einfach und schnell. Dank diesem Framework können wir Entwickler uns auf die Logik der API konzentrieren und mĂŒssen uns weniger um Dinge wie das Routing usw. kĂŒmmern. Danach verwenden wir die API unserer Ghost-Instanz.

Ghost war hinsichtlich der benutzerdefinierten Funktionen auf der Website nicht so flexibel, wie wir es uns gewĂŒnscht hĂ€tten. Deshalb mussten wir einen Ghost-Fork erstellen und unsere eigene Kopie bearbeiten. Beispiel: Der Kunde wollte die Anzeige bestimmter Inhalte vereinheitlichen und einfacher machen. FĂŒr gewöhnlich versucht man die Abspaltung (Fork) einer Codebasis zu vermeiden, aber sie brachte uns sehr viel mehr FlexibilitĂ€t, die wir sonst nicht gehabt hĂ€tten.

REST-API-Design mit Nest.js
Nest ist ein Node.js-Framework, das die Erstellung skalierbarer, serverseitiger Anwendungen vereinfacht. Als PHP-Entwicklerin habe ich mich anstelle einer PHP-Lösung, an die ich gewöhnt bin, fĂŒr Nest entschieden, weil das Team, mit dem ich dann arbeite, besser mit JavaScript zurechtkommt. FĂŒr mich ist der wichtigste Faktor bei der Wahl einer Technologie, wie einfach sie fĂŒr die Menschen ist, die sie warten mĂŒssen. Und wir sind wirklich froh darĂŒber, dass wir uns fĂŒr dieses Framework entschieden haben! Es hat wirklich grossen Spass gemacht, damit eine API zu erstellen.

Nest ist in Module unterteilt, wobei jedes Modul seine eigene Konfiguration wie auch seine eigenen Controller und Services/Provider hat. In unserem Projekt haben wir uns dazu entschieden, fĂŒr jede Integration ein eigenes Modul zu erstellen, das heisst also ein Vontobel-Modul, ein MigrosBank-Modul und ein Cap-Rechtsschutz-Modul. Wir haben auch noch ein paar weitere notwendige Module ergĂ€nzt, so zum Beispiel «Auth», «Mail», «Ghost», «Customer» ...

Wir haben einen ganzen Controller in Nest gebaut, um ein neues Ghost-Member zu schaffen:

import { Controller, Post, Body, Get } from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger';
import { GhostService } from './ghost.service';

@Controller('ghost')
@ApiTags('ghost')
export class GhostController {
  constructor(private readonly ghostService: GhostService) {}

  @Post('new')
  async newMember(@Body() body): Promise<any> {
    return this.ghostService.getGhostMemberUuidFromEmailCreateNewMemberIfNotExists(
      body.firstname,
      body.email,
    );
  }
}

In Nest verwenden wir viele Annotationen, was das Programmieren einfach und unkompliziert macht. Beachte die Verwendung von @Controller, um darauf hinzuweisen, dass es sich um einen Controller @ApiTags fĂŒr offene API-Dokumentationen und @Post fĂŒr die Route handelt!

Die Code-Injektion funktioniert aufgrund der Konfiguration im Modul:

import { Module } from '@nestjs/common';
import { HttpModule } from '@nestjs/axios';
import { GhostController } from './ghost.controller';
import { GhostService } from './ghost.service';

@Module({
  imports: [HttpModule],
  controllers: [GhostController],
  providers: [GhostService],
})
export class GhostModule {}

Beachte, dass wir dort einen «Ghost-Service» als Provider haben, also einen Service, den wir selbst entwickelt haben. Der Ghost-Service verfĂŒgt ĂŒber Methoden zur Kommunikation mit Ghost. Wir verwenden diese, um fĂŒr unsere Integrationen Ghost-Member mit den API-Membern zu verbinden und auch, um Member zu Friendly Automate hinzuzufĂŒgen, sobald sie sich registrieren.

TypeScript ist vollstĂ€ndig typisiert, was uns durch die Verwendung von Objekten fĂŒr API-Antworten ausserdem den Vorteil einer schönen Dokumentation mit Open API bietet. In den Objekten vermerkst du die Eigenschaften fĂŒr die Open API zur Ausgabe von Dokumentationen wie:

@ApiProperty({
    required: true,
    description:
      'The id that the frontend calling the ellexx api is using to identify their user, is usually a unique identifier',
    example: 'sdfsd-23432-sfdfsd-2323',
  })
  public frontendId: string;

Auf diese Weise kann eine Open API mit wenig Aufwand erstellt werden, bietet aber eine unschĂ€tzbare Dokumentation fĂŒr API-Consumer.

Infrastruktur

FĂŒr das Hosting wollten wir eine Schweizer Lösung und haben uns fĂŒr Managed Kubernetes von Exoscale entschieden. Ausserdem verwenden wir Longhorn fĂŒr MySQL. FĂŒr unser Projekt ist die Verwendung von Kubernetes definitiv ein Overkill, aber es funktioniert wunderbar. Wir nutzen GitLab, um unseren Code zu hosten und GitLab CI, um unseren Workflow zu automatisieren. Beim Pushen von Code wird ein Image erstellt und bereitgestellt. «to stage» fĂŒr den Stage-Branch und «to prod» fĂŒr den Haupt-Branch. Unsere Kubernetes-Lösung hat den Vorteil, dass sie skalierbar ist. ABER: Ghost ist nicht skalierbar. Dieses Problem solltest du bei einer viel besuchten Website mittels HTTP-Caching lösen. Wir haben sicherheitshalber einen einfachen Varnish-Cache erstellt. Die API ist hingegen skalierbar. Aufgrund der dynamischen Natur der Endpunkte können wir in diesem Fall keinen grossen Nutzen aus der Verwendung eines Cache ziehen.

Post-Mortem

HĂ€tten wir bei der Verwendung von Ghost CMS, Nest, Friendly Analytics, GitLab, GitLab CI, Kubernetes und MySQL mit Longhorn etwas anders machen können? Nein, eigentlich nicht! Wir sind sehr zufrieden mit unserem Set-up. Das Einzige, das wir möglicherweise hĂ€tten anders machen können, ist, Ghost als Headless CMS einzusetzen. Aber letztlich hat es auch so gut funktioniert, und wir sind alle sehr glĂŒcklich und stolz auf das, was wir erreicht haben.


Sag uns was du denkst