API Driven Development ist in aller Munde. Viele neue Frameworks sind zu 100% per API steuerbar oder haben zmd. ALLE Resourcen per API angebunden. Zeit für mich das ganze mal auszuprobieren und was eignet sich da besser als ein Symfony Projekt namens API Platform.
API Platform
REST and GraphQL framework on top of Symfony and React. (Quelle:https://api-platform.com/ ) Geradeaus ein API Framework. Schauen wir uns mal einige Features an die ziemlich cool sind:
User Management
Integriertes Management von Nutzern mit dem bekannten FOS Userbundle
OpenAPI (Swagger) Doku
Automatische Generierung von übersichtlichen API Dokus (hier mehr)
weitere Funktionen
Use Case
Was ist ein Test von neuer Software ohne Usecase für ein Test? Kein richtiger. Hello World und Tutorials kann jeder – also was machen? In letzter Zeit habe ich immer öfter Anfragen für Shopware Entwicklungen erhalten – um diese besser zu organisieren möchte ich ein kleines Tool schreiben, dass diese organisiert und an verschiedenen Stellen ausspielt:
Websites: wöchentliche Verfügbarkeit (rot / gelb / grün)
Da ich mehrere Websites hoste und verwalte (unter anderem dieser Blog) – macht es Sinn, dass ganze zentral zu verwalten und über eine API an alle Endpunkte auszuspielen.
Overkill? Ja. Aber ein guter Grund um zu lernen (ITler müssen das bis zum ENDE).
Installation (DOCKER)
Wir laden uns das letzte Stable Release HIER herunter und entpacken es. Danach gehts mit cd in den Ordner:
cd api-platform
Ich werde hier den Weg über Docker nutzen, musste aber dafür auf die neuste Version updaten und danach habe ich docker-compose pull ausgeführt und mit docker-compose up -d gestartet.
Tipp: Wenn Ihr den Port 80 schon für Apache oder NGINX nutzt – stoppt den Service kurz wenn Ihr nur kurz testen wollt oder ändert den Port in den Docker files.
Den obigen Screen sehen wir nach der Installation auf localhost – das Adminpanel und die API sind sofort abrufbar und enthalten erstmal nur die Resource Greetings
Installation (Symfony Flex)
Da ich schon öfter mit Symfony gearbeitet habe, möchte ich hier auch noch den üblichen Weg zeigen den ich hier auch empfehlen würde, gerade wenn man lokal schon entwickelt und den Port 80 schon nutzt.
composer create-project symfony/skeleton api-platform
cd api-platform
composer req api
Damit wurden bereits alle nötigen PHP Libraries installiert und Ihr müsst nur noch eure DB anbinden und das Model erstellen:
wie üblich in Symfony. Das Model hole ich mir einfach aus der aktuellen DB, das ist einfacher als alle getter und setter selbst zu generieren
php bin/console doctrine:mapping:import "App\Entity" annotation --path=src/Entity
und schon haben wir unser Model und müssen hier nur noch unseren API Stuff hinzufügen
Wenn ich nun meine URL (www.api.de/api/) aufrufe erhalte ich mein Model mit passendem Swagger UI. So far, so simple.
Nun sollten bereits alle API Requests laufen (CRUD).
API Admin Panel
Wenn Ihr ein Adminpanel wollt erstellt euch am besten ein eigenes Projekt in einem Subordner mkdir /var/www/api-admin/
Dort dann einfach mit yarn oder npm installieren:
npx create-react-app my-app
oder
yarn create react-app my-app
danach ändert Ihr einfach die URL in App.js und führt yarn start im Root Verzeichnis aus.
Schon läuft unsere React App gefüttert von der API
Model anpassen
Da wir in Symfony arbeiten können wir mit dem Doctrine Migrationbundle auch bequem unsere Datenbank über den diff anpassen – denn ich möchte nicht mehr KW und Jahr benutzen, sondern von und bis um größere Projekte zu blocken ohne 10 KW Einträge zu erstellen.
php bin/console doctrine:migrations:diff
php bin/console doctrine:migrations:execute --up 2019032518502
Authentifizierung
Nun soll das ganze ja nicht nur lokal laufen, also sollten wir unsere API mal schützen. Dafür bietet sich JWT (JSON Web Token), da API Platform dies auch in der Doku empfiehlt. Wir erstellen uns dazu ein User Model mit Symfony.
composer require symfony/security-bundle
composer require maker
php bin/console make:user
Was uns ein Model erstellt welches wir migrieren und danach ein Form für den Login generieren:
php bin/console doctrine:migrations:migrate
achtet darauf das Ihr mind. Mysql 5.7 oder MariaDB 10.2 habt, denn Ihr benötigt JSON als Typ.
php bin/console make:auth
erstellt euch zum Ende dann das Form für den Login.
TIPP: Erstellt euch ein Fixture für euren Nutzer.
<?php
namespace App\DataFixtures;
use App\Entity\User;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
class UserFixture extends Fixture
{
private $encoder;
public function __construct(UserPasswordEncoderInterface $encoder)
{
$this->encoder = $encoder;
}
public function load(ObjectManager $manager)
{
$user = new User();
$user->setEmail('test@gmail.com');
$encoded = $this->encoder->encodePassword($user, 'test1234');
$user->setPassword($encoded);
$user->setRoles(['ROLE_ADMIN']);
$manager->persist($user);
$manager->flush();
}
}
So könnt Ihr bequem euren Benutzer testen ohne vorher eine Registrierung zu bauen.
Ergebnis
Wir haben nun die normalen CRUD Funktionen für unser Verfügbarkeitsmodel.
Das ist schon mal eine gute Basis. Nun möchte ich aber auf verschiedenen Seiten diese Verfügbarkeit per KW und Jahr abfragen. Ich müsste mir also einen Custom Endpunkt bauen, dem ich diese beiden Werte gebe oder …
GraphQL
Api Platform bietet auch eine Implementierung von Graphql – einer Query Sprache für APIs. Da ich diese neue QL für die Zukunft der API halte, werde ich darauf in einem getrennten Artikel genauer eingehen.
Errors
Access to fetch at 'http://www.api.de/api' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The 'Access-Control-Allow-Origin' header has a value 'null' that is not equal to the supplied origin. Have the server send the header with a valid value, or, if an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
Das könnte auftreten, wenn Ihr keinen CORS_ALLOW_ORIGIN Wert eingetragen habt. Dieser sollte in der .env Datei stehen.
CORS_ALLOW_ORIGIN=^https?://admin.api.de(:[0-9]+)?$