• Czas czytania ~7 min
  • 08.11.2022

Przez lata, jako programiści, zawsze szukaliśmy sposobów na zautomatyzowanie naszej dokumentacji, od PHPDoc po Swagger i nie tylko. W ostatnich latach nastąpiła znacząca zmiana w świecie API polegająca na przyjęciu podejścia do dokumentacji API opartego na projektowaniu. Było to spowodowane głównie stworzeniem specyfikacji OpenAPI, która jest następcą Swaggera.

Jako programiści często nie mamy czasu ani ochoty zagłębiać się w podejście do naszych interfejsów API, które koncentruje się na projektowaniu — to po prostu fakt. Chociaż może to być najlepsza praktyka i to, co wszyscy mówią. W rzeczywistości płacimy za tworzenie i dostarczanie funkcji, więc często pomijamy te kroki. Co by było, gdybym powiedział Ci, że możesz dodać jeden mały krok do swojego przepływu pracy, który pozwoliłby Ci na tworzenie dokumentacji API na bieżąco?

Zacznijmy. W tym samouczku zbuduję całkowicie fikcyjny interfejs API bez żadnego celu i nie będzie to sposób, w jaki zwykle budowałbym interfejs API. Jest to jednak nie bez powodu, ponieważ chcę, abyś skupił się wyłącznie na krokach, które podejmuję.

Nie będę przechodzić przez początkowe kroki konfiguracji, ponieważ było to często omawiane w przeszłości. W tym samouczku przyjmę wiele założeń - głównie wokół ciebie, wiedząc, jak skonfigurować i zainstalować Laravel, ale także o użyciu artisan do generowania klas.

Mamy otwarty projekt Laravel w naszym IDE i musimy dodać trochę funkcjonalności. Uważam, że podczas tworzenia interfejsów API pomocne jest oszacowanie celu interfejsu API. Spróbuj zrozumieć, dlaczego jest budowany i po co jest budowany. Pozwala nam to zrozumieć, co będzie przydatne dla naszego API i powstrzymuje nas przed dodawaniem kolejnych punktów końcowych, które po prostu nie są potrzebne.

We will be building a simple książkihelf API in this tutorial. It will allow us to create some pretty basic functionality - so that we have a purpose for what we are building. This API aims at consumers who can connect to our API to manage their books. The most important thing to these users is a way to see their books and add books quickly. W końcu dostępne byłyby inne funkcje, ale zawsze uruchamiaj interfejs API z podstawowym celem, którego będzie potrzebować każdy użytkownik.

Utwórz nowy model, migrację i fabrykę dla modelu księgi. Będzie to podstawowy model w naszym API. Atrybuty, które chcesz mieć w tym modelu, nie są tak ważne w tym samouczku — ale i tak je omówię:

public function up(): void
{
    Schema::create('books', static function (Blueprint $table): void {
        $table->id();
 
        $table->string('title');
        $table->string('subtitle')->nullable();
        $table->text('description');
        $table->string('language');
 
        $table->unsignedBigInteger('pages');
 
        $table->json('authors');
        $table->json('categories');
        $table->json('images');
        $table->json('isbn');
 
        $table->date('published_at');
        $table->timestamps();
    });
}

Odzwierciedla to nieco interfejs API Google Books. Mamy tytuł książki i podtytuł, który jest nazwą książki. Opis i język wyjaśniają, o czym jest książka iw jakim języku. Mamy liczbę stron, aby zobaczyć, czy jest to duża książka. Następnie mamy autorów, kategorie, obrazy i numery ISBN, aby dodać dodatkowe informacje. Wreszcie jest data publikacji.

Teraz, gdy mamy model i bazę danych, możemy zacząć przyglądać się samemu interfejsowi API. Naszym pierwszym krokiem będzie instalacja pakietu, który pozwoli nam wygenerować naszą dokumentację API. Jest doskonały pakiet o nazwie Skryba którego możesz użyć, który działa zarówno w Laravel, jak i prostych aplikacjach PHP. Możesz go zainstalować za pomocą następującego polecenia kompozytora:

composer require --dev knuckleswtf/scribe

Once you have installed this, you can follow the setup instructions to get this working within your application. Once this is installed, and the configuration is published - you can do a test run of the API doc generation to ensure it works as expected. You should get something like this out of the box:

openapi: 3.0.3
info:
  title: Laravel
  description: ''
  version: 1.0.0
servers:
  -
    url: 'http://localhost:8000'
paths: []
tags: []

Teraz, gdy wiemy, że to działa, możemy dodać kilka tras, aby upewnić się, że będą one używane w generowaniu OpenAPI.

Zacznijmy od naszych pierwszych tras dotyczących pobierania książek, uzyskiwania listy książek i zdobywania jednej książki.

Route::prefix('books')->as('books:')->middleware(['api'])->group(static function (): void {
    Route::get(
        '/',
        App\Http\Controllers\Api\Books\IndexController::class,
    )->name(
        name: 'index',
    );
 
    Route::get(
        '{book}',
        App\Http\Controllers\Api\Books\ShowController::class,
    )->name(
        name: 'show',
    );
});

Mamy dwie trasy pod ul books przedrostek i oba są OTRZYMAĆ trasy, które nie wymagają uwierzytelnienia. Będą one częścią publicznego interfejsu API, do którego każdy będzie miał dostęp.

Teraz mamy trasy i kontrolery na miejscu. Musimy się zastanowić, jak chcemy obsłużyć te trasy. Chcemy sortować i filtrować nasze zapytania, abyśmy mogli poprosić o konkretne listy książek. Aby to osiągnąć, użyjemy tzw Konstruktor zapytań Space Laravel pakiet, zapewniając przejrzysty interfejs do wyszukiwania i filtrowania tego, czego potrzebujemy. Zainstalujmy to za pomocą następującego polecenia kompozytora:

composer require spatie/laravel-query-builder

Once this has been installed, we will be able to think about how to filter our API requests to get the correct list of books. With all good APIs, there is pagination. To achieve this, we could use the built-in paginator from Laravel. However, Aaron Francis created a paginator called Fast Paginate which is much more performant - something important when it comes to an API. You can install this using the following composer command:

composer require hammerstone/fast-paginate

Połączmy te dwie rzeczy, abyśmy mogli zbudować kolekcję i zwrócić wynik podzielony na strony.

return QueryBuilder::for(
    subject: Book::class,
)->allowedFilters(
    filters: ['language', 'pages', 'published_at'],
)->fastPaginate();

Działa to dobrze w naszym przypadku użycia — jednak nie jestem fanem zwracania wyników zapytań bezpośrednio z interfejsu API. Zawsze powinniśmy przekształcać odpowiedź za pomocą zasobu API do kontrolowanego formatu. Laravel ma wbudowane zasoby lub możesz użyć Fraktal lig PHP pakiet, który jest całkiem niezły. W tym przykładzie użyję pakietu, który posiadam pisano o wcześniej, więc nie będę wchodził w szczegóły. Ostatecznie powinniśmy otrzymać kontroler, który wygląda jak poniżej lub przynajmniej bardzo go przypomina:

final class IndexController
{
    public function __invoke(Request $request): JsonResponse
    {
        return new JsonResponse(
            data: BookResource::collection(
                resource: QueryBuilder::for(
                    subject: Book::class,
                )->allowedFilters(
                    filters: ['language', 'pages', 'published_at'],
                )->fastPaginate(),
            ),
        );
    }
}

Jak na razie to tylko zarejestruje naszą trasę w specyfikacji OpenAPI, co jest lepsze niż nic. Ale przy odrobinie dodatkowej pracy możemy udokumentować parametry i pogrupować rzeczy, co ma o wiele większy sens. W Scribe możesz to zrobić za pomocą DocBlocks lub Attributes. Wolę do tego używać DocBlocks, ponieważ nie ma atrybutów dla wszystkich pól, których chciałbyś użyć. Pozwól, że pokażę ci przykład i przeprowadzę cię przez wszystko, co zrobiłem.

final class IndexController
{
    /**
     * @group Book Collection
     *
     * Get all Books from the API.
     *
     * @queryParam filter[language] Filter the books to a specific language. filter[language]=en
     * @queryParam filter[pages] Filter the books to those with a certain amount of pages. filter[pages]=1000
     * @queryParam filter[published_at] Filter the books to those published on a certain date. filter[published_at]=12-12-1992
     *
     */
    public function __invoke(Request $request): JsonResponse
    {
        return new JsonResponse(
            data: BookResource::collection(
                resource: QueryBuilder::for(
                    subject: Book::class,
                )->allowedFilters(
                    filters: ['language', 'pages', 'published_at'],
                )->fastPaginate(),
            ),
        );
    }
}

Zaczynamy od dodania Kolekcja książek @group który zgrupuje ten punkt końcowy w „Kolekcji książek”, umożliwiając łatwiejszą nawigację po dokumentacji API. Następnie dodajemy opcję „Pobierz wszystkie książki z interfejsu API”. który opisuje konkretny punkt końcowy. Następnie możemy dodać dodatkowe @parametr zapytania wpisy w celu udokumentowania dostępnych parametrów zapytania, które przyjmuje ten punkt końcowy. O wiele łatwiejsze niż pisanie YAML, prawda!

Dokumentacja Scribe zawiera znacznie więcej informacji i możesz zagłębić się w dodawane informacje. Omówiłem tutaj tylko podstawy - ale już widać, jak przydatne może to być. Czego używasz do dokumentacji API? Daj nam znać na Twitterze!

Comments

No comments yet
Yurij Finiv

Yurij Finiv

Full stack

O

Professional Fullstack Developer with extensive experience in website and desktop application development. Proficient in a wide range of tools and technologies, including Bootstrap, Tailwind, HTML5, CSS3, PUG, JavaScript, Alpine.js, jQuery, PHP, MODX, and Node.js. Skilled in website development using Symfony, MODX, and Laravel. Experience: Contributed to the development and translation of MODX3 i...

O autorze CrazyBoy49z
WORK EXPERIENCE
Kontakt
Ukraine, Lutsk
+380979856297