# Getting Started with API Platform: Hypermedia and GraphQL API, Admin and Progressive Web App ![The welcome page](images/api-platform-2.2-welcome.png) > *API Platform* is the most advanced API platform, in any framework or language. > > —Fabien Potencier (creator of Symfony), SymfonyCon 2017 [API Platform](https://api-platform.com) is a powerful but easy to use **full stack** framework dedicated to API-driven projects. It contains a **PHP** library to create fully featured APIs supporting industry-leading standards (JSON-LD, GraphQL, OpenAPI...), provides ambitious **JavaScript** tooling to consume those APIs in a snap (admin, PWA and mobile app generators, hypermedia client...) and is shipped with a nice **Docker** and **Kubernetes** integration to develop and deploy instantly on the cloud. The easiest and most powerful way to get started is to download the API Platform distribution. It contains: * an API skeleton, including with [the server-side component](../core/index.md), [the Symfony 4 microframework](https://symfony.com/doc/current/setup/flex.html) and [the Doctrine ORM](http://docs.doctrine-project.org/projects/doctrine-orm/) * a dynamic JavaScript admin, leveraging the hypermedia capabilities of API Platform and built on top of [React](https://reactjs.org/) and [React Admin](https://marmelab.com/react-admin/) * a Progressive Web App skeleton, generated with [Create React App](https://github.com/facebookincubator/create-react-app) and containing the tools to scaffold your own React/[Redux](https://redux.js.org/) app in one command * a [Docker](https://docker.com)-based setup to bootstrap the project in a single command, providing: * servers for the API and JavaScript apps * a [Varnish Cache](http://varnish-cache.org/) server enabling [API Platform's built-in invalidation cache mechanism](../core/performance.md#enabling-the-built-in-http-cache-invalidation-system) * a development HTTP/2 and HTTPS proxy (allowing, for instance, to test the provided [service workers](https://developer.mozilla.org/fr/docs/Web/API/Service_Worker_API)) * a [Helm](https://helm.sh/) chart to deploy the API in any [Kubernetes](https://kubernetes.io/) cluster To discover how the framework works, we will create an API to manage a bookshop. To create a fully featured API, an admin interface and a Progressive Web App, we will only have to design the public data model of our API and handcraft it as *Plain Old PHP Objects*. API Platform uses these model classes to expose a web API having a bunch of built-in features: * creating, retrieving, updating and deleting (CRUD) resources * data validation * pagination * filtering * sorting * hypermedia/[HATEOAS](https://en.wikipedia.org/wiki/HATEOAS) and content negotiation support ([JSON-LD](http://json-ld.org), [HAL](http://blog.stateless.co/post/13296666138/json-linking-with-hal), [JSON API](http://jsonapi.org/)) * [GraphQL support](http://graphql.org/) * Nice UI and machine-readable documentations ([Swagger/OpenAPI](https://swagger.io), [Hydra](http://hydra-cg.com)) * authentication ([Basic HTTP](https://en.wikipedia.org/wiki/Basic_access_authentication), cookies as well as [JWT](https://jwt.io/) and [OAuth](https://oauth.net/) through extensions) * [CORS headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS) * security checks and headers (tested against [OWASP recommendations](https://www.owasp.org/index.php/REST_Security_Cheat_Sheet)) * invalidation-based HTTP caching * and basically everything needed to build modern APIs. One more thing, before we start: as the API Platform distribution includes [the Symfony framework](https://symfony.com), it is compatible with most [Symfony bundles](https://symfony.com/blog/the-30-most-useful-symfony-bundles-and-making-them-even-better) (plugins) and benefits from the numerous extensions points provided by this rock-solid foundation (events, DIC...). Adding features like custom, service-oriented, API endpoints, JWT or OAuth authentication, HTTP caching, mail sending or asynchronous jobs to your APIs is straightforward. ## Installing the Framework ### Using the API Platform Distribution (Recommended) Start by [downloading the API Platform distribution `.tar.gz` file](https://github.com/api-platform/api-platform/releases/latest), or [generate a GitHub repository from the template we provide](https://github.com/api-platform/api-platform/generate). Once you have extracted its contents, the resulting directory contains the API Platform project structure. You will add your own code and configuration inside it. **Note**: Try to avoid using the `.zip` file, as it may cause potential [permission](https://github.com/api-platform/api-platform/issues/319#issuecomment-307037562) [issues](https://github.com/api-platform/api-platform/issues/777#issuecomment-412515342). API Platform is shipped with a [Docker](https://docker.com) setup that makes it easy to get a containerized development environment up and running. If you do not already have Docker on your computer, [it's the right time to install it](https://docs.docker.com/install/). On Mac, only [Docker for Mac](https://docs.docker.com/docker-for-mac/) is supported. Similarly, on Windows, only [Docker for Windows](https://docs.docker.com/docker-for-windows/) is supported. Docker Machine **is not** supported out of the box. Open a terminal, and navigate to the directory containing your project skeleton. Run the following command to start all services using [Docker Compose](https://docs.docker.com/compose/): $ docker-compose pull # Download the latest versions of the pre-built images $ docker-compose up -d # Running in detached mode This starts the following services: | Name | Description | Port(s) | Environment(s) | ----------- | ------------------------------------------------------------- | ------- | -------------- | php | The API with PHP, PHP-FPM 7.2, Composer and sensitive configs | n/a | all | db | A PostgreSQL database server | 5432 | all (prefer using a managed service in prod) | client | A development server for the Progressive Web App | 80 | dev (use a static website hosting service in prod) | admin | A development server for the admin | 81 | dev (use a static website hosting service in prod) | api | The HTTP server for the API (Nginx) | 8080 | all | cache-proxy | A HTTP cache proxy for the API provided by Varnish | 8081 | all (prefer using a managed service in prod) | mercure | Mercure hub | 1337 | all | h2-proxy | A HTTP/2 and HTTPS development proxy for all apps | 443 (client)
444 (admin)
8443 (api)
8444 (cache-proxy) | dev (configure properly your web server in prod) To see the container's logs, run: $ docker-compose logs -f # follow the logs Project files are automatically shared between your local host machine and the container thanks to a pre-configured [Docker volume](https://docs.docker.com/engine/tutorials/dockervolumes/). It means that you can edit files of your project locally using your preferred IDE or code editor, they will be transparently taken into account in the container. Speaking about IDEs, our favorite software to develop API Platform apps is [PHPStorm](https://www.jetbrains.com/phpstorm/) with its awesome [Symfony](https://confluence.jetbrains.com/display/PhpStorm/Getting+Started+-+Symfony+Development+using+PhpStorm) and [Php Inspections](https://plugins.jetbrains.com/plugin/7622-php-inspections-ea-extended-) plugins. Give them a try, you'll got auto-completion for almost everything and awesome quality analysis. The API Platform distribution comes with a dummy entity for test purpose: `api/src/Entity/Greeting.php`. We will remove it later. If you're used to the PHP ecosystem, you probably guessed that this test entity uses the industry-leading [Doctrine ORM](http://www.doctrine-project.org/projects/orm.html) library as persistence system. It is shipped, in the API Platform distribution. Doctrine ORM is the easiest way to persist and query data in an API Platform project thanks to the bridge shipped with the distribution. It is optimized for performance and development convenience. For instance, when using Doctrine, API Platform is able to automatically optimize the generated SQL queries by adding the appropriate `JOIN` clauses. It also provides a lot of powerful built-in filters. Doctrine ORM and its bridge support most popular RDBMS including PostgreSQL, MySQL, MariaDB, SQL Server, Oracle and SQLite. There is also a shipped [Doctrine MongoDB ODM](https://www.doctrine-project.org/projects/mongodb-odm.html) optional support. That being said, keep in mind that API Platform is 100% independent of the persistence system. You can use the one(s) that best suit(s) your needs (including NoSQL databases or remote web services). API Platform even supports using several persistence systems together in the same project. ### Using Symfony Flex and Composer (advanced users) Alternatively, the API Platform server component can also be installed directly on a local machine. **This method is recommended only for advanced users who want full control over the directory structure and the installed dependencies.** The rest of this tutorial assumes that you have installed API Platform using the official distribution. Go straight to the next section if it's your case. API Platform has an official Symfony Flex recipe. It means that you can easily install it from any Flex-compatible Symfony application using [Composer](https://getcomposer.org/): # Create a new Symfony Flex project $ composer create-project symfony/skeleton bookshop-api # Enter the project folder $ cd bookshop-api # Install the API Platform's server component in this skeleton $ composer req api Then, create the database and its schema: $ bin/console doctrine:database:create $ bin/console doctrine:schema:create And start the built-in PHP server or the Symfony WebServerBundle: # Built-in PHP server $ php -S 127.0.0.1:8000 -t public # Symfony WebServerBundle $ composer req server --dev $ bin/console server:run All JavaScript components are also [available as standalone libraries](https://github.com/api-platform?language=javascript) installable with NPM or Yarn. **Note:** when installing API Platform this way, the API will be exposed as the `/api/` path. You need to open `http://localhost:8000/api/` to see the API documentation. If you are deploying API Platform directly on an Apache or Nginx webserver and getting a 404 error on opening this link, you will need to enable the [rewriting rules](https://symfony.com/doc/current/setup/web_server_configuration.html) for your specific webserver software. ## It's Ready! Open `https://localhost` in your favorite web browser: ![The welcome page](images/api-platform-2.2-welcome.png) You'll need to add a security exception in your browser to accept the self-signed TLS certificate that has been generated for this container when installing the framework. Repeat this step for all other services available through HTTPS. Later you will probably replace this welcome screen by the homepage of your Progressive Web App. If you don't plan to create a Progressive Web App, you can remove the `client/` directory and the related lines in `docker-compose.yaml` (don't do it now, we'll use this container later in this tutorial). Click on the "HTTPS API" button, or go to `https://localhost:8443/`: ![The API](images/api-platform-2.2-api.png) API Platform exposes a description of the API in the [OpenAPI](https://www.openapis.org/) format (formerly known as Swagger). It also integrates a customized version of [Swagger UI](https://swagger.io/swagger-ui/), a nice interface rendering the Open API documentation. Click on an operation to display its details. You can also send requests to the API directly from the UI. Try to create a new *Greeting* resource using the `POST` operation, then access it using the `GET` operation and, finally, delete it by executing the `DELETE` operation. If you access any API URL using a web browser, API Platform detects it (by scanning the `Accept` HTTP header) and displays the corresponding API request in the UI. Try it yourself by browsing to `http://localhost:8080/greetings`. If the `Accept` header doesn't contain `text/html` as the preferred format, a JSON-LD response is sent ([configurable behavior](../core/content-negotiation.md)). So, if you want to access the raw data, you have two alternatives: * Add the correct `Accept` header (or don't set any `Accept` header at all if you don't care about security) - preferred when writing API clients * Add the format you want as the extension of the resource - for debug purpose only For instance, go to `http://localhost:8080/greetings.jsonld` to retrieve the list of `Greeting` resources in JSON-LD, or to `http://localhost:8080/greetings.json` to retrieve data in raw JSON. Of course, you can also use your favorite HTTP client to query the API. We are fond of [Postman](https://www.getpostman.com/). It works perfectly well with API Platform, has native Open API support, allows to easily write functional tests and has good team collaboration features. ## Bringing your Own Model Your API Platform project is now 100% functional. Let's expose our own data model. Our bookshop API will start simple. It will be composed of a `Book` resource type and a `Review` one. Books have an id, an ISBN, a title, a description, an author, a publication date and are related to a list of reviews. Reviews have an id, a rating (between 0 and 5), a body, an author, a publication date and are related to one book. Let's describe this data model as a set of Plain Old PHP Objects (POPO) and map it to database tables using annotations provided by the Doctrine ORM: ```php reviews = new ArrayCollection(); } public function getId(): ?int { return $this->id; } } ``` ```php id; } } ``` **Tip**: you can also use Symfony [MakerBundle](https://symfony.com/doc/current/bundles/SymfonyMakerBundle/index.html) thanks to the `--api-resource` option: $ docker-compose exec php bin/console make:entity --api-resource As you can see there are two typical PHP objects with the corresponding PHPDoc (note that entities's and properties's descriptions included in their PHPDoc will appear in the API documentation). Doctrine's annotations map these entities to tables in the database. Annotations are convenient as they allow grouping the code and the configuration but, if you want to decouple classes from their metadata, you can switch to XML or YAML mappings. They are supported as well. Learn more about how to map entities with the Doctrine ORM in [the project's official documentation](http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html) or in Kévin's book "[Persistence in PHP with the Doctrine ORM](https://www.amazon.fr/gp/product/B00HEGSKYQ/ref=as_li_tl?ie=UTF8&camp=1642&creative=6746&creativeASIN=B00HEGSKYQ&linkCode=as2&tag=kevidung-21)". For the sake of simplicity, in this example we used public properties (except for the id, see below). API Platform as well as Doctrine also support accessor methods (getters/setters), use them if you want to. We used a private property for the id and a getter for the id to enforce the fact that it is read only (the ID will be generated by the RDMS because of the `@ORM\GeneratedValue` annotation). API Platform also has first-grade support for UUIDs. [You should probably use them instead of auto-incremented ids](https://www.clever-cloud.com/blog/engineering/2015/05/20/why-auto-increment-is-a-terrible-idea/). Then, delete the file `api/src/Entity/Greeting.php`. This demo entity isn't useful anymore. Finally, tell Doctrine to sync the database tables structure with our new data model: $ docker-compose exec php bin/console doctrine:schema:update --force The `php` container is where your API app stands. Prefixing a command by `docker-compose exec php` allows to execute the given command in this container. You may want [to create an alias](http://www.linfo.org/alias.html) to make your life easier. Later, you'll want to use [Doctrine Migrations](https://symfony.com/doc/current/doctrine.html#migrations-creating-the-database-tables-schema) when changing the database's structure. We now have a working data model that you can persist and query. To create an API endpoint with CRUD capabilities corresponding to an entity class, we just have to mark it with an annotation called `@ApiResource`: ```php