Introducing API Platform (beta): the next generation PHP web framework

API Platform

API Platform

Update: API Platform now has a dedicated website with an up to date version of the following tutorial.

PHP celebrates its 20 years this week. In 20 years, the web changed dramatically and is now evolving faster than ever:, Symfony, Facebook and many others have worked hard to improve and professionalize the PHP ecosystem. The PHP world has closed the gap with most backend solutions and is often more innovative than them.

But in critical area I’ve described previously, many things can be improved. Almost all existing solutions are still designed and documented to create websites the old way: a server generate then send plain-old HTML documents to browsers.

What a better gift for the PHP birthday than a brand new set of tools to kickstart modern web projects? Here comes Dunglas’s API platform, a framework for API-first projects built on top of Symfony! Like other modern frameworks such as Zend Framework and Symfony, it’s both a full-stack all-in-one framework and a set of independent PHP components and bundles that can be used separately.

The architecture promoted by the framework will distrust many of you but read until the end and you will see how API Platform make modern development easy and fun again:

  • Start by creating a hypermedia REST API exposing structured data that can be understood by any compliant client such your apps but also as search engines (JSON-LD with vocabulary). This API is the central and unique entry point to access and modify data. It also encapsulates the whole business logic.
  • Then create as many clients as you want using frontend technologies you love: an HTML5/Javascript webapp querying the API in AJAX (of course) but also a native iOS or Android app, or even a desktop application. Clients only display data and forms.

Let’s see how to do that. In this tutorial, we will create a typical blog application with API Platform.

If you are in a hurry, a demo is available online and all sources created during this tutorial are available on GitHub:


To create the API-side of our project we will:

  • Bootstrap a fully featured and working data model including ORM mapping, validation rules and semantic metadata with the generator provided by API platform (of course you can also handcraft your data model or modify the generated one to fit your needs).
  • Expose this data model trough a read/write (CRUD) API following JSON-LD and Hydra Core Vocabulary open standards, having metadata and with a ton of features out of the box: pagination, validation, error serialization, filters and a lot of other awesome features (here too, everything is extensible thanks to a powerful event system and strong OOP).

Then we will develop a tiny AngularJS webapp to illustrate how to create and consume data from the API. Keep in mind that you can use your preferred client-side technology (tested and approved with Angular, React, Ionic, Swift but can work with any language able to send HTTP requests).


Only PHP 5.5+ must be installed to run Dunglas’s API Platform. A built-in web server is shipped with the framework for the development environment.

To follow this tutorial a database must be installed (but its not a strong dependency of the framework). I recommend MySQL or MariaDB but other major DBMS are supported including SQLite, PostgreSQL, Oracle and SQL server are supported trough Doctrine.

Installing the framework

Let’s start our new blog API project. The easiest way to create a new project is to use Composer (you need to have it installed on your box):

Composer creates the skeleton of the new blog API then retrieve the framework and all its dependencies.

At the end of the installation, you will be prompted for some configuration parameters including database credentials. All configuration parameters can be changed later by editing the app/config/parameters.yml file.

Dunglas’s API Platform is preconfigured to use the popular Doctrine ORM. It is supported natively by all API Platform components. However the Doctrine ORM is fully optional: you can replace it by your favorite ORM, no ORM at all and even no database.

The installer will ask for:

  • mail server credentials (to send mails)
  • the locale of the application
  • the URL of your default web client application to automatically set appropriate CORS headers, set it to http://locahost:9000  (the default URL of the built-in Grunt server) to follow this tutorial
  • a name and a description of the API that will be used in the generated documentation
  • a secret token (choose a long one) for cryptographic features

Take a look at the content of the generated directory. You should recognize a Symfony application directory structure. It’s fine and intended: the generated skeleton is a perfectly valid Symfony full-stack application that follows Symfony Best Practices. It means that you can:

The skeleton comes with a demonstration bookstore API. Remove it:

  • empty  app/config/schema.yml and  app/config/services.yml
  • delete all files in the src/AppBundle/Entity/ directory

Generating the data model

The first incredibly useful tool provided by Dunglas’s API platform is it’s data model generator also know as PHP Schema. This API Platform component can also be used standalone to bootstrap any PHP data model.

To kickstart our blog data model we browse and find an existing schema that describe perfectly what we want:

The  schema command line tool will instantly generate a PHP data model from the vocabulary:

Browse, choose the types and properties you need (there is a bunch of schemas available), run our code generator. You’re done! You get a fully featured PHP data model including:

  • A set of PHP entities with properties, constants (enum values), getters, setters, adders and removers. The class hierarchy provided by will be translated to a PHP class hierarchy with parents as abstract classes. The generated code complies with PSR coding standards.
  • Full high-quality PHPDoc for classes, properties, constants and methods extracted from
  • Doctrine ORM annotation mapping including database columns with type guessing, relations with cardinality guessing, class inheritance (through the @AbstractSuperclass annotation).
  • Data validation through Symfony Validator annotations including data type validation, enum support (choices) and check for required properties.
  • Interfaces and Doctrine ResolveTargetEntityListener support.
  • List of values provided by with PHP Enum classes.

Reusing an existing semantic schema has many advantages:

Don’t Reinvent The Wheel

Data models provided by are popular and have been proved efficient. They cover a broad spectrum of topics including creative work, e-commerce, event, medicine, social networking, people, postal address, organization, place or review. has its root in a ton of preexisting well designed vocabularies and is successfully used by more and more website and applications.

Pick up schemas applicable to your application, generate your PHP model, then customize and specialize it to fit your needs.

Improve SEO and user experience

Adding markup to websites and apps increase their ranking in search engines results and enable awesome features such as Google Rich Snippets and Gmail markup.

Mapping your app data model to structures can be a tedious task. Using the generator, your data model will be a derived from Serializing your data as JSON-LD will not require specific mapping nor adaptation. It’s a matter of minutes.

Be ready for the future improves the interoperability of your applications. Used with hypermedia technologies such as Hydra it’s a big step towards the semantic and machine readable web. It opens the way to generic web API clients able to extract and process data from any website or app using such technologies.
To generate our data model form types, we must create a YAML configuration file for PHP schema:

Then execute the generator:

Take a look at the content of the src/AppBundle/Entity/ directory. PHP Schema generated for us a set of Plain-Old-PHP entities representing our data model. As promised our entities include:

  • type documentation from and converted it to PHPDoc
  • Doctrine ORM mapping annotations (including for relations)
  • Symfony validation annotations
  • IRI mapping (the @Iri annotations), we will see later that the API bundle use them to expose structured semantic data
  • and they follow the PSR-2 coding style

The data model is fully functional. You can hack it (modify entities, properties, indexes, validation rules…), or use it as is!

Ask Doctrine to create the database of the project:

Then generate database tables related to the generated entities:

PHP Schema provides a lot of configuration options. Take a look at its dedicated documentation. Keep in mind that PHP Schema is also available as a standalone tool (and a PHAR will be available soon) and can be used to bootstrap any PHP project (works fine with raw PHP, API Platform and Symfony but has an extension mechanism allowing to use it with other technologies such as Zend Framework and Laravel).

Sometimes we will have to make a data model with very specific business types, not available in Sometimes we will find types that partially matches what we want but needs to be adapted.

Keep in mind that you can always create your very own data model from scratch. It’s perfectly OK and you can still use API Platform without PHP Schema.

Anyway, PHP Schema is a tool intended to bootstrap the data model. You can and you will edit manually generated PHP entities. When you start to edit manually the generated files, be careful to not run the generator again, it will overwrite your changes (this behavior will be enhanced in future versions). When you do such things, the best to do is to remove dunglas/php-schema  from  your composer.json  file.

Exposing the API

We have a working data model backed by a database. Now we will create a hypermedia REST API thanks to another component of Dunglas’s API Platform: DunglasApiBundle.

As PHP Schema, it is already preinstalled and properly configured. We just need to declare resources we want to expose.

Exposing a resource collection basically consist to register a new Symfony service. For our blog app we will expose trough the API the two entity classes generated by PHP Schema: BlogPosting (blog post) and Person  (author of the post):

And our API is already finished! How would it be easier?

Start the integrated development web server:  app/console server:start

Then open http://localhost:8000/doc with a web browser:

API Platform doc

Thanks to NelmioApiDocBundle support of DunglasApiBundle and its integration with API Platform, you get for a free an automatically generated human-readable documentation of the API (Swagger-like). The doc also includes a sandbox to try the API.

You can also use your favorite HTTP client to query the API. I strongly recommend Postman. It is lower level than the sandbox and will allow to inspect forge and inspect JSON requests and responses easily.

Open  http://localhost:8000 with Postman. This URL is the entry point of the API. It gives to access to all exposed resources. As you can see, the API returns minified JSON-LD. For better readability, JSON snippets have been prettified in this document.

Trying the API

Add a person named Kévin by issuing a POST request on  http://localhost:8000/users  with the following JSON document as raw body:

The data is inserted in database. The server replies with a JSON-LD representation of the freshly created resource. Thanks to PHP Schema, the  @type  property of the JSON-LD document is referencing a type:

The JSON-LD spec is fully supported by the bundle. Want a proof? Browse http://localhost:8000/contexts/Person .

By default, the API allows GET (retrieve, on collections and items), POST (create), PUT (update) and DELETE (self-explaining) HTTP methods. You can add and remove any other operation you want. Try it!

Now, browse http://localhost:8000/people :

Pagination is also supported (and enabled) out of the box.

It’s time to post our first article. Run a POST request on http://locahost:8000/blog_posting with the following JSON document as body:

Oops… the isFamilyFriendly  property is a boolean. Our JSON contains an incorrect string. Fortunately the bundle is smart enough to detect the error: it uses Symfony validation annotations generated by PHP Schema previously. It returns a detailed error message in the Hydra error serialization format:

Correct the body and send the request again:

We fixed it! By the way you learned how to to work with relations. In a hypermedia API, every resource is identified with an unique IRI (an URL is a an IRI). They are in the @id  property of every JSON-LD document generated by the API and you can use it as reference to set relations like we done in the previous snippet for the author property.

Dunglas’s API Platform is smart enough to understand any date format supported by PHP date functions. In production we recommend the format specified by the RFC 3339.

We already have a powerful hypermedia REST API (always without writing a single line of PHP), but there is more.

Our API is auto-discoverable. Open http://localhost:8000/vocab and take a look at the content. Capabilities of the API are fully described in a machine-readable format: available resources, properties and operations, description of elements, readable and writable properties, types returned and expected…

As for errors, the whole API is described using the Hydra Core Vocabulary, an open web standard for describing hypermedia REST APIs in JSON-LD. Any Hydra-compliant client or library is able to interact with the API without knowing anything about it! The most popular Hydra client is Hydra Console. Open an URL of the API with it you’ll get a nice management interface.

API Platform Hydra console

You can also give a try to the brand new hydra-core Javascript library.

DunglasApiBundle offers a lot of other features including:

Read its dedicated documentation to see how to leverage them and how to hook your own code everywhere into it.

Specifying and testing the API

Behat (a Behavior-driven development framework) is preconfigured with contexts useful to spec and test REST API and JSON documents.

With Behat, you can write the API specification (as user stories) in natural language then execute scenarios against the application to validate its behavior.

Create a Gherkin feature file containing the scenarios we run manually in the previous chapter:

The API Platform flavor of Behat also comes with a temporary SQLite database dedicated to tests. It works out of the box.

Just run bin/behat . Everything should be green:

Then you get a powerful hypermedia API exposing structured data, specified and tested thanks to Behat. And still without a line of PHP!

It’s incredibly useful for prototyping and Rapid Application Development (RAD). But the framework is designed to run in prod. It benefits from strong extension points and is has been optimized for very high-traffic websites (API Platform powers the new version of a major world-wide media site).

Other features

Dunglas’s API Platform has a lot of other features and can extended with PHP libraries and Symfony bundles. Stay tuned, more documentation and cookbooks are coming!

Here is a non exhaustive list of what you can do with API Platform:

The Angular client

Prequistes: Node.js and NPM must be installed.

Dunglas’s API Platform is agnostic of the client-side technology. You can use whatever platform, language or framework you want. As an illustration of doors opened by an API-first architecture and the ease of development of SPA, we will create a tiny AngularJS client.

Keep in mind that this is not an AngularJS tutorial and it doesn’t pretend to follow AngularJS best practices. It’s just an example of how simple application development become when all the business logic is encapsulated in an API. The unique responsibility of our AngularJS blog client is to display informations retrieved by the API (presentation layer).

The AngularJS application is fully independent of the API. It’s a HTML/JS/CSS app querying an API trough AJAX. It leaves in its own Git repository and is hosted on its own web server. As it only contains assets, it can even be hosted directly on a CDN such as Amazon CloudFront or Akamai.

To scaffold our AngularJS app we will use the official Angular generator of Yeoman. Install the generator then generate a client skeleton:

Yeoman will ask some questions. We want to keep the app minimal:

  • don’t install Sass support
  • install Twitter Bootstrap (it’s optional but the app will look better)
  • uncheck all suggested angular modules

However, we will install Restangular, an awesome REST client for Angular that fit well with API Platform:

The Angular generator comes with the Grunt build tool. It compiles assets (minification, compression) and can serve the web app with an integrated development web server. Start it:

DunglasApiBundle provides a Restangular integration guide in its documentation. Once configured, Restangular will work with the JSON-LD/Hydra API like with any other standard REST API.

Then edit app/app.js file to register Restangular and configure it properly:

Be sure to change the base URL with the one of your API in production (protip: use grunt-ng-constant).

And here is the controller retrieving the posts list and allowing to create a new one:

As you can see, querying the API with Restangular is easy and very intuitive. The library automatically issues HTTP requests to the server and hydrates “magic” JavaScript objects corresponding to JSON responses that can be manipulated to modify remote resources (trough PUT , POST  and DELETE requests).

We also leverage the server side error handling to display beautiful messages when submitted data are invalid or when something goes wrong.

And the corresponding view looping over posts and displaying the form and errors:

It’s the end of this tutorial. Our blog client is ready and working! Remember of the old way of doing synchrone web apps? What do you think of this new approach? Easier and very much powerful isn’t it?

Of course there are some tasks to have a finished client including routing, pagination support and injecting raw JSON-LD in the generated HTML for search engines (use the response extractor hook provided by Restangular). As it’s only Angular tasks, it’s out of scope of this introduction to API Platform. But it’s a good exercise to add such features to the client. Feel free to share your snippets in comments.

Convinced of the facility of development and of the power of API Platform? Let us know your feedback in comments!


The first beta is ready and there is some task to do before the first stable release:

  • The code is almost ready and already used awesome compagnies including, Smile, the Trip Advisor group and ExaqtWorld. But API Platform still need to bullet proof. The first task before the release is: testing and hunting bugs.
  • The documentation must be enhanced. This tutorial will be the base of the brand new doc being written. A dedicated website is also in progress.
  • We also need more integration (doc or libraries) with popular frontend technologies!

I hope to release the first stable version in July. You can help by contributing (bug reports, docs, code). Everything is hosted on GitHub and Pull Requests are very welcome.


Special thanks to Samuel Roze (formerly and now Inviqa), Théo Fidry and Fabien Gasser (Smile), Maxime Steinhausser and Jérémy Derussé (SensioLabs) and Michaël Labrut (La Fourchette) for contributing code, docs and ton of good ideas.

API Platform would never have emerged without the great technologies and libraries it uses as foundation, especially Symfony, Doctrine JSON-LD, Hydra and A big thanks to all their contributors.

Commercial support

Feared of trying this new framework? Commercial support is already available:

  • Contact me for application design and architecture consulting.
  • can develop your softwares based on API Platform (and Symfony) and its team of great engineers do training and consulting.


  1. Doing the “Get Started” tutorial I was unable to generate the data model. I got this error:
    [error] The type “SocialMediaPosting” (parent of “BlogPosting”) doesn’t exist

Leave a Reply