API Platform 2.5

API Platform 2.5: revamped Admin, new API testing tool, Next.js and Quasar app generators, PATCH and JSON Schema support, improved OpenAPI and GraphQL support

I’m very excited to announce the immediate availability of API Platform 2.5!

API Platform is a set of standalone server and client components for building and consuming REST (JSON-LD, Hydra, JSON:API…) and GraphQL APIs. The server components use PHP and Symfony while the client-side components (which support any Hydra-enabled web API, even the ones not built using API Platform) are written in JS. If you haven’t tried API Platform yet, it only takes a few minutes to create your first project!

API Platform 2.5 is the best version ever of the framework! Let’s discover its most interesting new features.

API Platform Admin 1.0: the power of React Admin and Material UI unleashed!

API Platform comes with a powerful component to create admin interfaces. It is built on top of React Admin and Material-UI.

Pass the URL of any Hydra-enabled API (including, of course, APIs built with API Platform itself) to the HydraAdmin component, and you instantly get a beautiful, fully-featured admin interface for resources exposed by the API including:

  • CRUD pages;
  • widgets based on the type of the properties;
  • filters;
  • sorting;
  • client-side validation…

The admin interface is dynamically built client-side, by parsing the Hydra metadata of the API. To do so, only a single LLOC is necessary:

import React from "react";
import { HydraAdmin } from "@api-platform/admin";

export default () => <HydraAdmin entrypoint="https://demo.api-platform.com"/>

And you get this (live demo):

demo-admin.api-platform.com

In previous versions, customizing the UI wasn’t straightforward nor idiomatic: you had to post-process the JS object containing the parsed API documentation.

Luckily, the Marmelab (creator of React Admin) and Les-Tilleuls.coop (creator of API Platform) teams got together several times during the summer to create a new version which dramatically improves the DX. Thus API Platform Admin 1.0 was born! While keeping the simplicity of previous versions, it now allows to leverage the power of JSX to customize every part of the UI using React Admin components, Material-UI components, or your custom React components:

import React from "react";
import {
  HydraAdmin,
  ResourceGuesser,
  CreateGuesser,
  InputGuesser
} from "@api-platform/admin";
import { ReferenceInput, AutocompleteInput } from "react-admin";

const ReviewsCreate = props => (
  <CreateGuesser {...props}>
    <InputGuesser source="author" />
    <ReferenceInput
      source="book"
      reference="books"
      label="Books"
      filterToQuery={searchText => ({ title: searchText })}
    >
      <AutocompleteInput optionText="title" />
    </ReferenceInput>

    <InputGuesser source="rating" />
    <InputGuesser source="body" />
  </CreateGuesser>
);

export default () => (
  <HydraAdmin entrypoint="https://demo.api-platform.com">
    <ResourceGuesser
      name="reviews"
      create={ReviewsCreate}
    />
  </HydraAdmin>
);

In the previous example, we add an autocomplete input for a relation by using the dedicated React Admin component while letting API Platform Admin guess which inputs to use for the other properties based on the API docs.

Check out the updated and improved documentation to learn more about these new customization capabilities!

This feature has been contributed by Morgan Auchede, Florian Ferbach, Gildas Garcia, Alexis Janvier, Jean-François Thuillier, François Zaninotto and myself.

Next.js and Quasar support in the Client Generator

API Platform also provides an app generator supporting React, Vue and React Native. And in version 2.5, it is also able to generate high quality Next.js and Quasar apps!

API Platform Client Generator is very similar to Symfony’s MakerBundle and to Rails’ scaffolding, but instead of generating server-side code, it generates a client-side app which uses the API to fetch and persist data. To do so, like the admin component, the generator uses the Hydra specification exposed by the API (built with API Platform or otherwise; support for OpenAPI and GraphQL is in progress).

Next.js is the most popular framework for React, with automatic Server-Side Rendering support, routing, code splitting, CSS-in-JS, static exporting… The Next.js generator, created by Grégory Copin, leverages the 0-config TypeScript support introduced in Next 9: all generated code is written in TS!

To generate a Next app from any API exposing a Hydra specification, run the following commands:

$ npm install next --save # Install next
$ npx @api-platform/client-generator https://demo.api-platform.com src/ --generator next # Scaffold the app

And you get high quality code you can customize to fit your needs:

List screenshot

Refer to the dedicated documentation to learn about all the capabilities of this generator.

Quasar is a framework built on top of Vue.js allowing to write SPAs with Server-Side Rendering support, mobile apps and Electron apps in one go. Its comes with a nice Material Design interface.

To use it, run the following commands:

$ npm install -g @quasar/cli
$ quasar create my-app
$ npx @api-platform/client-generator https://demo.api-platform.com src/ --generator quasar my-app/src # Scaffold the app

A big thanks to Paul Apostol for contributing this generator!

A brand new test client and dedicated API assertions

Currently, there is a lack of satisfactory solutions to write functional tests for web APIs built in PHP (API Platform and Symfony included):

  • Behat+Behatch+Mink is BDD-oriented (which is completely fine, but doesn’t fit with all projects/teams), is a bit complex to set up, has a high barrier to entry and is still not fully compatible with Symfony 4 (you need to rely on a dev version of Behat). It also lacks some utilities, for database testing for instance.
  • BrowserKit/WebTestCase is dedicated to webpage testing through web scraping (DOM Crawler, CSS selectors, simulation of browser actions such as clicking or reloading a page…). Its API doesn’t fit well with API testing. However, it benefits from the large ecosystem of PHPUnit, and gives access to numerous functional testing helpers provided by Symfony.
  • External solutions which don’t manipulate the Symfony Kernel (but are true HTTP clients) such as Postman or Blackfire Player require setting up a web server for the testing environment and don’t provide access to the service container (for instance, to test if the database has been updated, or if a mail has been sent).

It’s time to say hi to ApiTestCase, Test\Client and a bunch of new assertions dedicated to API testing (matching a JSON document or a subset of it, checking the status code and headers of HTTP responses, validating against a  JSON Schema…)! This new set of API testing utilities is built on top of Symfony’s KernelTestCase class, and the Test\Client implements the exact same interface as the brand new Symfony HttpClient component.

This new testing tool also plays very well with Alice (a test fixture generator having an official Symfony/API Platform recipe), which has also gained new powers in the process!

Let’s see how it looks:

namespace App\Tests;

use ApiPlatform\Core\Bridge\Symfony\Bundle\Test\ApiTestCase;
use App\Entity\Book;
use Hautelook\AliceBundle\PhpUnit\RefreshDatabaseTrait;

class BooksTest extends ApiTestCase
{
    // This trait provided by HautelookAliceBundle will take care of refreshing the database content to a known state before each test
    use RefreshDatabaseTrait;
    
    public function testCreateBook(): void
    {
        $response = static::createClient()->request('POST', '/books', ['json' => [
            'isbn' => '0099740915',
            'title' => 'The Handmaid\'s Tale',
            'description' => 'Brilliantly conceived and executed, this powerful evocation of twenty-first century America gives full rein to Margaret Atwood\'s devastating irony, wit and astute perception.',
            'author' => 'Margaret Atwood',
            'publicationDate' => '1985-07-31T00:00:00+00:00',
        ]]);

        $this->assertResponseStatusCodeSame(201);
        $this->assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8');
        $this->assertJsonContains([
            '@context' => '/contexts/Book',
            '@type' => 'Book',
            'isbn' => '0099740915',
            'title' => 'The Handmaid\'s Tale',
            'description' => 'Brilliantly conceived and executed, this powerful evocation of twenty-first century America gives full rein to Margaret Atwood\'s devastating irony, wit and astute perception.',
            'author' => 'Margaret Atwood',
            'publicationDate' => '1985-07-31T00:00:00+00:00',
            'reviews' => [],
        ]);
        $this->assertRegExp('~^/books/\d+$~', $response->toArray()['@id']);

        // This new assertions checks that the returned JSON document matches the JSON Schema generated by API Platform for this resource (also included in the OpenAPI file) 
        $this->assertMatchesResourceItemJsonSchema(Book::class);
    }

    public function testUpdateBook(): void
    {
        $client = static::createClient();
        // findIriBy allows to retrieve the IRI of an item by searching for some of its properties.
        // ISBN 9786644879585 has been generated by Alice when loading test fixtures.
        // Because Alice use a seeded pseudo-random number generator, we're sure that this ISBN will always be generated.
        $iri = static::findIriBy(Book::class, ['isbn' => '9781344037075']);

        $client->request('PUT', $iri, ['json' => [
            'title' => 'updated title',
        ]]);

        $this->assertResponseIsSuccessful();
        $this->assertJsonContains([
            '@id' => $iri,
            'isbn' => '9781344037075',
            'title' => 'updated title',
        ]);
    }

    public function testDeleteBook(): void
    {
        $client = static::createClient();
        $iri = static::findIriBy(Book::class, ['isbn' => '9781344037075']);

        $client->request('DELETE', $iri);

        $this->assertResponseStatusCodeSame(204);
        $this->assertNull(
            // Through the container, you can access all your services from the tests, including the ORM, the mailer, remote API clients...
            static::$container->get('doctrine')->getRepository(Book::class)->findOneBy(['isbn' => '9781344037075'])
        );
    }
}

Check out the full documentation, or the screencast which has been published on SymfonyCasts on this topic.

Improved GraphQL support

The GraphQL subsystem has been dramatically improved in version 2.5. Kudos to Alan Poulain who has done an awesome job maintaining and improving this part, and to Lukas Lücke and Mahmood Bazdar for their great contributions.

The main features of this new version are the customization of queries, mutations and types.

Whenever you need to add your own logic in your schema, you can now add custom properties in your resource’s annotations to do it. For instance to add a custom query:

/**
 * @ApiResource(graphql={
 *     "myShinyQuery"={
 *         "item_query"=MyQueryItemResolver::class,
 *         "args"={
 *             "id"={"type"="ID"},
 *             "isShiny"={"type"="Boolean!"}
 *         }
 *     }
 * })
 */

You only need to define the corresponding resolver and it’s done!

<?php

namespace App\Resolver;

use ApiPlatform\Core\GraphQl\Resolver\QueryItemResolverInterface;

final class MyQueryItemResolver implements QueryItemResolverInterface
{
    public function __invoke($item, array $context)
    {
        // Do what you want!

        return $item;
    }
}

Please refer to the documentation for customizing the mutations and the types.

The new version also comes with:

  • a better pagination mechanism (including backwards pagination)
  • a new “stage” mechanism allowing to easily customize the built-in resolvers, and an easier way to customize behaviors using decoration
  • support for GraphQL Playground, in addition to GraphiQL
  • a new command to export the GraphQL schema automatically generated by API Platform
  • support for file uploads
  • support for name converters
  • improved overall performance

All these new features have been documented, check it out!

JSON Merge Patch Support

Until now, API Platform had only supported using the PUT HTTP method for replacing a resource. Supporting proper partial updates through the PATCH method is known to be hard.

In version 2.5, a large refactoring work has been done to properly support the PATCH method, and support for JSON Merge Patch (RFC 7386) has been added (to do so, we’ve contributed the required low-level brick to the Symfony Serializer component). The new infrastructure is designed to support other PATCH formats as well. In the future, other formats such as JSON Patch (RFC 6902) may be added.
The JSON:API PATCH format, which was already supported, is of course still working.

Read the dedicated documentation entry to learn more about API Platform’s PATCH support (including how to enable it for existing projects).

JSON Schema Support

JSON Schema is a popular vocabulary to describe the shape of JSON documents. A variant of JSON Schema is also used in OpenAPI specifications. As of API Platform 2.5, JSON Schema is a first-class citizen. A new infrastructure has been created to be able to generate JSON Schemas for any resource, represented in any format (including JSON-LD). A command has also been added to export these schemas:

$ bin/console api:json-schema:generate 'App\Entity\Review'

The generated schema can be used with libraries such as react-json-schema-form to build forms for the documented resources, or to be used for validation.

To generate JSON Schemas programmatically, use the new api_platform.json_schema.schema_factory service.

Improved OpenAPI support

Thanks to the improvements made in the OpenAPI v3 specification, and to the new JSON Schema infrastructure in API Platform, the generated OpenAPI documentation is now better. It generates a specific JSON Schema per supported format:

JSON-LD specific keys such as @id and @context are now documented.

Grégoire Hebert added the ability to configure the versions of OpenAPI to support, while also  allowing to specify the default version being used:

api_platform:
    swagger:
        versions: [3, 2] # OpenAPI v3 is now the default

Frédéric Barthelet and Ryan Weaver improved the OpenAPI documentation of the built-in order and property filters. Thank you!

Screencasts and improved docs

We are very proud to announce an official partnership between API Platform and SymfonyCasts! SymfonyCasts has published more than 7 hours of high quality and funny video tutorials on API Platform (and counting!). The screencasts are now available directly from the main menu of the website, and referenced in the appropriate documentation entries.

Also, for version 2.5, API Platform’s docs have been dramatically improved. All new features presented in this post (and more) are now documented; many parts of the docs have been fixed, improved and modernized; sections have been reordered to provide a better learning experience; and a new “extending API Platform” entry has been added to centralize how to hook your custom logic in a way which is compatible with both the REST and GraphQL subsystems. But there is still a lot to do, and your help is very welcome!

Community milestones and Hacktoberfest

The main strength of API Platform is its vibrant and diverse community! Recently, the project reached great milestones thanks to the many contributors, evangelists and users improving and promoting the framework: the Slack channel reached 2 000 users a few days ago; the core package of the framework has been downloaded more than 2 000 000 times; and workshops, meetups and talks about the project are being organized all over the world (next big events with API Platform talks: Forum PHP Paris and SymfonyCon Amsterdam)!

The framework is close to reaching 5 000 stars on GitHub! It will be the next big milestone, and we’ll organize a big party to celebrate! If you have not starred API Platform yet, please do! It will help us reach a wider audience. If you love the project, tell your friends about it!

Speaking about community, free software and contribution, Hacktoberfest is about to begin! Hacktoberfest is an event helping free software projects by encouraging contributions (code but also docs and marketing materials). As thanks for your contribution, Digital Ocean offers a limited-edition t-shirt, and nice stickers. This year, Les-Tilleuls.coop will also send you some surprise gifts if you contribute at least 3 Pull Requests on the API Platform repositories. Thanks Les-Tilleuls!

Hacktoberfest is a very good opportunity to start contributing to free software, and to API Platform in particular. To help you get started, we’ve marked some code and docs issues with a specific “Hacktoberfest” label:

If you need help working on this, don’t hesitate to ask us directly on GitHub, or in the API Platform Slack channel!

https://lh3.googleusercontent.com/-6RjHzvBuxFo/XZH8jV3YDtI/AAAAAAAADWE/kMKfpexyNWMGC3JMgv0Gp4XfDZxghfe-ACK8BGAsYHg/s0/Image%2Bfrom%2BiOS%2B%252812%2529.jpg