├── admin
├── images
│ └── admin-demo.gif
├── schema.org.md
├── index.md
├── authentication-support.md
├── getting-started.md
├── handling-relations-to-collections.md
└── customizing.md
├── core
├── images
│ ├── JWTAuthorizeButton.png
│ ├── JWTConfigureApiKey.png
│ ├── NelmioApiDocBundle.png
│ ├── SerializerWorkflow.png
│ ├── deprecated-graphiql.png
│ ├── mercure-discovery.png
│ ├── deprecated-swagger-ui.png
│ ├── mercure-subscriptions.png
│ └── diagrams
│ │ ├── api-platform-get-i-o.dia
│ │ ├── api-platform-get-i-o.png
│ │ ├── api-platform-put-i-o.dia
│ │ ├── api-platform-put-i-o.png
│ │ ├── api-platform-post-i-o.dia
│ │ └── api-platform-post-i-o.png
├── nelmio-api-doc.md
├── json-schema.md
├── default-order.md
├── push-relations.md
├── index.md
├── operation-path-naming.md
├── external-vocabularies.md
├── data-persisters.md
├── angularjs-integration.md
├── design.md
├── extending-jsonld-context.md
├── form-data.md
├── identifiers.md
├── errors.md
├── fosuser-bundle.md
├── testing.md
├── mercure.md
├── deprecations.md
├── extending.md
├── mongodb.md
├── messenger.md
├── elasticsearch.md
├── extensions.md
├── subresources.md
├── configuration.md
└── events.md
├── distribution
├── images
│ ├── swagger-ui-1.png
│ ├── swagger-ui-2.png
│ ├── api-platform-2.5-api.png
│ ├── symfonycasts-player.png
│ ├── api-platform-2.5-admin.png
│ ├── api-platform-2.5-graphql.png
│ ├── api-platform-2.5-pwa-react.png
│ ├── api-platform-2.5-welcome.png
│ └── api-platform-2.5-bookshop-api.png
└── debugging.md
├── .github
├── ISSUE_TEMPLATE
│ ├── 2_Documentation_issue.md
│ └── 1_Support_question.md
└── PULL_REQUEST_TEMPLATE.md
├── client-generator
├── images
│ ├── client-generator-demo.gif
│ ├── react
│ │ ├── client-generator-react-edit.png
│ │ ├── client-generator-react-list.png
│ │ ├── client-generator-react-show.png
│ │ ├── client-generator-react-delete.png
│ │ └── client-generator-react-list-pagination.png
│ ├── nextjs
│ │ ├── client-generator-nextjs-list.png
│ │ └── client-generator-nextjs-show.png
│ └── react-native
│ │ ├── client-generator-react-native-add.png
│ │ ├── client-generator-react-native-list.png
│ │ ├── client-generator-react-native-show.png
│ │ └── client-generator-react-native-delete.png
├── typescript.md
├── nextjs.md
├── index.md
├── quasar.md
├── vuejs.md
├── troubleshooting.md
├── react-native.md
├── react.md
└── vuetify.md
├── .proselintrc
├── extra
├── contribution-guides.md
├── releases.md
├── conduct.md
├── philosophy.md
└── troubleshooting.md
├── .editorconfig
├── deployment
├── index.md
├── heroku.md
├── kubernetes.md
└── docker-compose.md
├── .travis.yml
├── outline.yaml
└── schema-generator
└── index.md
/admin/images/admin-demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fredericbarthelet/docs/HEAD/admin/images/admin-demo.gif
--------------------------------------------------------------------------------
/core/images/JWTAuthorizeButton.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fredericbarthelet/docs/HEAD/core/images/JWTAuthorizeButton.png
--------------------------------------------------------------------------------
/core/images/JWTConfigureApiKey.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fredericbarthelet/docs/HEAD/core/images/JWTConfigureApiKey.png
--------------------------------------------------------------------------------
/core/images/NelmioApiDocBundle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fredericbarthelet/docs/HEAD/core/images/NelmioApiDocBundle.png
--------------------------------------------------------------------------------
/core/images/SerializerWorkflow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fredericbarthelet/docs/HEAD/core/images/SerializerWorkflow.png
--------------------------------------------------------------------------------
/core/images/deprecated-graphiql.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fredericbarthelet/docs/HEAD/core/images/deprecated-graphiql.png
--------------------------------------------------------------------------------
/core/images/mercure-discovery.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fredericbarthelet/docs/HEAD/core/images/mercure-discovery.png
--------------------------------------------------------------------------------
/core/images/deprecated-swagger-ui.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fredericbarthelet/docs/HEAD/core/images/deprecated-swagger-ui.png
--------------------------------------------------------------------------------
/core/images/mercure-subscriptions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fredericbarthelet/docs/HEAD/core/images/mercure-subscriptions.png
--------------------------------------------------------------------------------
/distribution/images/swagger-ui-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fredericbarthelet/docs/HEAD/distribution/images/swagger-ui-1.png
--------------------------------------------------------------------------------
/distribution/images/swagger-ui-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fredericbarthelet/docs/HEAD/distribution/images/swagger-ui-2.png
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/2_Documentation_issue.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: 📄 Documentation issue
3 | about: Report a documentation issue
4 |
5 | ---
6 |
--------------------------------------------------------------------------------
/core/images/diagrams/api-platform-get-i-o.dia:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fredericbarthelet/docs/HEAD/core/images/diagrams/api-platform-get-i-o.dia
--------------------------------------------------------------------------------
/core/images/diagrams/api-platform-get-i-o.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fredericbarthelet/docs/HEAD/core/images/diagrams/api-platform-get-i-o.png
--------------------------------------------------------------------------------
/core/images/diagrams/api-platform-put-i-o.dia:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fredericbarthelet/docs/HEAD/core/images/diagrams/api-platform-put-i-o.dia
--------------------------------------------------------------------------------
/core/images/diagrams/api-platform-put-i-o.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fredericbarthelet/docs/HEAD/core/images/diagrams/api-platform-put-i-o.png
--------------------------------------------------------------------------------
/distribution/images/api-platform-2.5-api.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fredericbarthelet/docs/HEAD/distribution/images/api-platform-2.5-api.png
--------------------------------------------------------------------------------
/distribution/images/symfonycasts-player.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fredericbarthelet/docs/HEAD/distribution/images/symfonycasts-player.png
--------------------------------------------------------------------------------
/core/images/diagrams/api-platform-post-i-o.dia:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fredericbarthelet/docs/HEAD/core/images/diagrams/api-platform-post-i-o.dia
--------------------------------------------------------------------------------
/core/images/diagrams/api-platform-post-i-o.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fredericbarthelet/docs/HEAD/core/images/diagrams/api-platform-post-i-o.png
--------------------------------------------------------------------------------
/distribution/images/api-platform-2.5-admin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fredericbarthelet/docs/HEAD/distribution/images/api-platform-2.5-admin.png
--------------------------------------------------------------------------------
/client-generator/images/client-generator-demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fredericbarthelet/docs/HEAD/client-generator/images/client-generator-demo.gif
--------------------------------------------------------------------------------
/distribution/images/api-platform-2.5-graphql.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fredericbarthelet/docs/HEAD/distribution/images/api-platform-2.5-graphql.png
--------------------------------------------------------------------------------
/distribution/images/api-platform-2.5-pwa-react.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fredericbarthelet/docs/HEAD/distribution/images/api-platform-2.5-pwa-react.png
--------------------------------------------------------------------------------
/distribution/images/api-platform-2.5-welcome.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fredericbarthelet/docs/HEAD/distribution/images/api-platform-2.5-welcome.png
--------------------------------------------------------------------------------
/distribution/images/api-platform-2.5-bookshop-api.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fredericbarthelet/docs/HEAD/distribution/images/api-platform-2.5-bookshop-api.png
--------------------------------------------------------------------------------
/client-generator/images/react/client-generator-react-edit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fredericbarthelet/docs/HEAD/client-generator/images/react/client-generator-react-edit.png
--------------------------------------------------------------------------------
/client-generator/images/react/client-generator-react-list.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fredericbarthelet/docs/HEAD/client-generator/images/react/client-generator-react-list.png
--------------------------------------------------------------------------------
/client-generator/images/react/client-generator-react-show.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fredericbarthelet/docs/HEAD/client-generator/images/react/client-generator-react-show.png
--------------------------------------------------------------------------------
/client-generator/images/nextjs/client-generator-nextjs-list.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fredericbarthelet/docs/HEAD/client-generator/images/nextjs/client-generator-nextjs-list.png
--------------------------------------------------------------------------------
/client-generator/images/nextjs/client-generator-nextjs-show.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fredericbarthelet/docs/HEAD/client-generator/images/nextjs/client-generator-nextjs-show.png
--------------------------------------------------------------------------------
/client-generator/images/react/client-generator-react-delete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fredericbarthelet/docs/HEAD/client-generator/images/react/client-generator-react-delete.png
--------------------------------------------------------------------------------
/.proselintrc:
--------------------------------------------------------------------------------
1 | {
2 | "checks": {
3 | "typography.symbols": false,
4 | "typography.exclamation": false,
5 | "hyperbole.misc": false,
6 | "cliches.misc": false
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/client-generator/images/react/client-generator-react-list-pagination.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fredericbarthelet/docs/HEAD/client-generator/images/react/client-generator-react-list-pagination.png
--------------------------------------------------------------------------------
/client-generator/images/react-native/client-generator-react-native-add.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fredericbarthelet/docs/HEAD/client-generator/images/react-native/client-generator-react-native-add.png
--------------------------------------------------------------------------------
/client-generator/images/react-native/client-generator-react-native-list.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fredericbarthelet/docs/HEAD/client-generator/images/react-native/client-generator-react-native-list.png
--------------------------------------------------------------------------------
/client-generator/images/react-native/client-generator-react-native-show.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fredericbarthelet/docs/HEAD/client-generator/images/react-native/client-generator-react-native-show.png
--------------------------------------------------------------------------------
/client-generator/images/react-native/client-generator-react-native-delete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fredericbarthelet/docs/HEAD/client-generator/images/react-native/client-generator-react-native-delete.png
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/1_Support_question.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: ⛔ Support Question
3 | about: See https://api-platform.com/support/ for questions about using API Platform
4 |
5 | ---
6 |
7 | We use GitHub issues only to discuss about bugs and new features.
8 | For this kind of questions about using API Platform, please use
9 | any of the support alternatives shown in https://api-platform.com/support/
10 |
11 | Thanks!
12 |
--------------------------------------------------------------------------------
/extra/contribution-guides.md:
--------------------------------------------------------------------------------
1 | # Contribution guides
2 |
3 | 1. [API Platform Core Library](https://github.com/api-platform/core/blob/master/CONTRIBUTING.md)
4 | 2. [API Platform Schema Generator](https://github.com/api-platform/schema-generator/blob/master/CONTRIBUTING.md)
5 | 3. [API Platform Admin](https://github.com/api-platform/admin/blob/master/CONTRIBUTING.md)
6 | 4. [API Platform CRUD Generator](https://github.com/api-platform/client-generator/blob/master/CONTRIBUTING.md)
7 |
8 |

Watch the Contributing back to Symfony screencast (free-
9 |
--------------------------------------------------------------------------------
/extra/releases.md:
--------------------------------------------------------------------------------
1 | # The Release Process
2 |
3 | API Platform follows the [Semantic Versioning](https://semver.org) strategy.
4 |
5 | Only 3 versions are maintained at the same time:
6 |
7 | * **stable** (currently the **2.5** branch): regular bug fixes are integrated in this version
8 | * **old-stable** (currently **2.4** branch): security fixes are integrated in this version, regular bug fixes are **not** backported in it
9 | * **development** (**master** branch): new features target this branch
10 |
11 | Older versions (1.x, 2.0...) **are not maintained**. If you still use them, you must upgrade as soon as possible.
12 |
13 | The **old-stable** branch is merged in the **stable** branch on a regular basis to propagate security fixes.
14 | The **stable** branch is merged in the **development** branch on a regular basis to propagate security and regular bug fixes.
15 |
16 | New versions of API Platform are released **when they are ready**, on the behalf of the API Platform Core Team.
17 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # editorconfig.org
4 |
5 | root = true
6 |
7 | [*]
8 | # Change these settings to your own preference
9 | indent_style = space
10 | indent_size = 4
11 |
12 | # We recommend you to keep these unchanged
13 | end_of_line = lf
14 | charset = utf-8
15 | trim_trailing_whitespace = true
16 | insert_final_newline = true
17 |
18 | [*.json]
19 | indent_style = space
20 | indent_size = 2
21 |
22 | [*.md]
23 | trim_trailing_whitespace = false
24 |
25 | [*.neon]
26 | indent_style = tab
27 | indent_size = 4
28 |
29 | [*.xml]
30 | indent_style = space
31 | indent_size = 4
32 |
33 | [*.{yaml,yml}]
34 | indent_style = space
35 | indent_size = 2
36 | trim_trailing_whitespace = false
37 |
38 | [.circleci/config.yml]
39 | indent_style = space
40 | indent_size = 2
41 |
42 | [.github/workflows/*.yml]
43 | indent_style = space
44 | indent_size = 2
45 |
46 | [.gitmodules]
47 | indent_style = tab
48 |
49 | [.proselintrc]
50 | indent_style = space
51 | indent_size = 2
52 |
53 | [.travis.yml]
54 | indent_style = space
55 | indent_size = 2
56 |
--------------------------------------------------------------------------------
/client-generator/typescript.md:
--------------------------------------------------------------------------------
1 | # TypeScript Interfaces
2 |
3 | The TypeScript Generator allows you to create [TypeScript interfaces](https://www.typescriptlang.org/docs/handbook/interfaces.html) that you can embed in any TypeScript-enabled project (React, Vue.js, Angular..)
4 |
5 | To do so, run the client generator:
6 |
7 | $ npx @api-platform/client-generator --generator typescript https://demo.api-platform.com src/ --resource foo
8 | # Replace the URL with the entrypoint of your Hydra-enabled API
9 | # "src/" represents where the interfaces will be generated
10 | # Omit the resource flag to generate files for all resource types exposed by the API
11 |
12 | This command parses the Hydra documentation and creates one `.ts` file for each API Resource you have defined in your application, in the `interfaces` subfolder.
13 |
14 | NOTE: If you are not sure what the entrypoint is, see [Troubleshooting](troubleshooting.md)
15 |
16 | ## Example
17 |
18 | Assuming you have 2 resources in your application, `Foo` and `Bar`, when you run
19 |
20 | $ npx @api-platform/client-generator --generator typescript https://demo.api-platform.com src/
21 |
22 | you will obtain 2 `.ts` files arranged as following:
23 |
24 | * src/
25 | * interfaces/
26 | * foo.ts
27 | * bar.ts
28 |
--------------------------------------------------------------------------------
/core/nelmio-api-doc.md:
--------------------------------------------------------------------------------
1 | # NelmioApiDocBundle Integration
2 |
3 | NelmioApiDoc provides an alternative to [the native Swagger/Open API support](swagger.md) provided by API Platform.
4 |
5 | As NelmioApiDocBundle 3+ has built-in support for API Platform, this documentation is only relevant for people using
6 | NelmioApiDocBundle between version 2.9 and 3.0.
7 |
8 | For new projects, prefer using the built-in Swagger support and/or NelmioApiDoc 3.
9 |
10 | 
11 |
12 | [NelmioApiDocBundle](https://github.com/nelmio/NelmioApiDocBundle) is supported by API Platform since version 2.9.
13 |
14 | To enable the NelmioApiDoc integration, copy the following configuration:
15 |
16 | ```yaml
17 | # api/config/packages/api_platform.yaml
18 | api_platform:
19 | # ...
20 |
21 | enable_nelmio_api_doc: true
22 |
23 | nelmio_api_doc:
24 | sandbox:
25 | accept_type: 'application/json'
26 | body_format:
27 | formats: ['json']
28 | default_format: 'json'
29 | request_format:
30 | formats:
31 | json: 'application/json'
32 | ```
33 |
34 | Please note that NelmioApiDocBundle has a sandbox limitation where you cannot pass a JSON array as parameter, so you cannot
35 | use it to deserialize nested objects.
36 |
--------------------------------------------------------------------------------
/core/json-schema.md:
--------------------------------------------------------------------------------
1 | # JSON Schema Support
2 |
3 | [JSON Schema](https://json-schema.org/) is a popular vocabulary to describe the shape of JSON documents. A variant of JSON Schema is also used [in OpenAPI specifications](swagger.md).
4 |
5 | API Platform provides an infrastructure to generate JSON Schemas for any resource, represented in any format (including JSON-LD).
6 | The generated schema can be used with libraries such as [react-json-schema-form](https://github.com/rjsf-team/react-jsonschema-form) to build forms for the documented resources, or to [be used for validation](https://json-schema.org/implementations.html#validators).
7 |
8 | ## Generating a JSON Schema
9 |
10 | To export the schema corresponding to an API Resource, run the following command:
11 |
12 | $ docker-compose exec php bin/console api:json-schema:generate 'App\Entity\Book'
13 |
14 | To see all options available, try:
15 |
16 | $ docker-compose exec php bin/console help api:json-schema:generate
17 |
18 | ## Generating a JSON Schema Programmatically
19 |
20 | To generate JSON Schemas programmatically, use the `api_platform.json_schema.schema_factory` [service](https://symfony.com/doc/current/service_container.html#fetching-and-using-services).
21 |
22 | ## Testing
23 |
24 | API Platform provides a PHPUnit assertion to test if a response is valid according to a given Schema: `assertMatchesJsonSchema()`.
25 | Refer to [the testing documentation](testing.md) for more details.
26 |
--------------------------------------------------------------------------------
/client-generator/nextjs.md:
--------------------------------------------------------------------------------
1 | # Next.js Generator
2 |
3 | 
4 |
5 | The Next.js Client Generator generates components for Server Side Rendered applications using [Next.js](https://zeit.co/blog/next)
6 |
7 | ## Install
8 |
9 | ### Next + Express Server
10 |
11 | Create a [Next.js application with express server](https://github.com/zeit/next.js/tree/canary/examples/custom-server-express). The easiest way is to execute:
12 |
13 | $ npx create-next-app your-app-name
14 | # or
15 | $ yarn create next-app your-app-name
16 |
17 | ### Installing the Generator Dependencies
18 |
19 | Enable TypeScript in your next project
20 |
21 | $ yarn add --dev typescript @types/react @types/node
22 |
23 | Install required dependencies:
24 |
25 | $ yarn add lodash.get lodash.has @types/lodash isomorphic-unfetch
26 |
27 | ## Generating Routes
28 |
29 | $ npx @api-platform/client-generator https://demo.api-platform.com . --generator next --resource book
30 | # Replace the URL by the entrypoint of your Hydra-enabled API
31 |
32 | > Note: Omit the resource flag to generate files for all resource types exposed by the API.
33 |
34 | ## Starting the Project
35 |
36 | You can launch the server with
37 |
38 | $ yarn dev
39 |
40 | Go to `https://localhost:3000/books/` to start using your app.
41 |
42 | ## Screenshots
43 |
44 | 
45 | 
46 |
--------------------------------------------------------------------------------
/client-generator/index.md:
--------------------------------------------------------------------------------
1 | # The API Platform Client Generator
2 |
3 | Client Generator is the fastest way to scaffold fully featured webapps and native mobile apps from APIs supporting the [Hydra](http://www.hydra-cg.com/) format.
4 |
5 | 
6 |
7 | *Generated React and React Native apps, updated in real time*
8 |
9 | It is able to generate apps using the following frontend stacks:
10 |
11 | * [React with Redux](react.md)
12 | * [React Native](react-native.md)
13 | * [Next.js](nextjs.md)
14 | * [Vue.js](vuejs.md)
15 | * [Quasar Framework](quasar.md)
16 |
17 | Client Generator works especially well with APIs built with the [API Platform](https://api-platform.com) framework.
18 |
19 | ## Features
20 |
21 | * Generates high-quality ES6:
22 | * list view (with pagination)
23 | * detail view
24 | * creation form
25 | * update form
26 | * delete button
27 | * Supports to-one and to-many relations
28 | * Uses the appropriate input type (`number`, `date`...)
29 | * Client-side validation
30 | * Subscribes to data updates pushed by servers supporting [the Mercure protocol](https://mercure.rocks)
31 | * Displays server-side validation errors under the related input (if using API Platform Core)
32 | * Integration with [Bootstrap](https://getbootstrap.com/) and [FontAwesome](https://fontawesome.com/) (Progressive Web Apps)
33 | * Integration with [React Native Elements](https://react-native-training.github.io/react-native-elements/)
34 | * Accessible to people with disabilities ([ARIA](https://www.w3.org/WAI/intro/aria) support in webapps)
35 |
--------------------------------------------------------------------------------
/deployment/index.md:
--------------------------------------------------------------------------------
1 | # Deploying API Platform Applications
2 |
3 | API Platform apps are super easy to deploy in production on most cloud providers thanks to the native integration with
4 | [Kubernetes](kubernetes.md).
5 |
6 | The server part of API Platform is basically a standard Symfony application, which you can also easily [deploy on your
7 | own servers](http://symfony.com/doc/current/deployment.html). Documentation for deploying on various container [orchestration](https://en.wikipedia.org/wiki/Orchestration_(computing))
8 | tools and PaaS (Platform as a Service) are also available:
9 |
10 | * [Deploying to a Kubernetes Cluster](kubernetes.md)
11 | * [Deploying with Docker Compose](docker-compose.md)
12 | * [Deploying on Heroku](heroku.md)
13 | * [Deploying on Platform.sh](https://platform.sh/blog/deploy-api-platform-on-platformsh)
14 |
15 | The clients are [Create React App](https://github.com/facebook/create-react-app/) skeletons. You can deploy them in a wink
16 | on any static website hosting service (including [Netlify](https://www.netlify.com/), [Firebase Hosting](https://firebase.google.com/docs/hosting/),
17 | [GitHub Pages](https://pages.github.com/), or [Amazon S3](https://docs.aws.amazon.com/en_us/AmazonS3/latest/dev/WebsiteHosting.html)
18 | by following [the relevant documentation](https://facebook.github.io/create-react-app/docs/deployment).
19 |
20 | 
Watch the Animated Deployment with Ansistrano screencast
21 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - 'lts/*'
4 |
5 | cache:
6 | yarn: true
7 | directories:
8 | - $HOME/.cache/pip
9 |
10 | env:
11 | global:
12 | secure: gjiWHIgNjLXuEFmLiepiOTHOuauHfeKHutrE0sBwFZUQzP9FoGTZzJub1z8/vm+hhygA+TZbB0iclygHyNrXCkZyNdnZXChXl4iPdYqY3OARPOAbQff16+/XSUDsZ/Ok1etdb3kKor1R4rBt/WywBvXmmdggumTA3yT5ExI+dyomdONo4yMUZ7g1la0ehMEzGqyVjt0nUW31PN3l6dI1qgigHCuotSrrpWP6fTXuUh5l6YA7KXb/V1hJxaGENLz1Cdfk0sF66e4KsV/DX6JZSqpvdqVB8OTPU+si511yJtGS8OeuLs4RqmXMLrY/ChCodlYnfCE+NleBFpUnCVqth/RDRh7LvplUJlpsXNcfyoA3mOFmZa0euOMCqJ3qwESz802Y9c5oN63hn2OUF/raFDc3SMMC86FFHxwyYjz5+yzXYupnFNj39NKdQ1v1KBbY8BD8UT8RU3mlu4/3LRz0tSamREHj3pGgBmvgUZfUE1dMngeaZjOmBaIZH8TnKQ75CvfnoT+LJnZo6g9g4uwz6jtaIziSMZ0OW/95nF8yx+xONbHEtt5ex5M09NOFsN2vB2bcUAgIjyGrmmNLQadwJyQv557IGPEE5CyGhJpXh9XZ+WMw2vO45MOw4sQPkARF6OJkMteqFc2NLXMOQJ07EScbgEKR/9VOcyxIk/7a7nc=
13 |
14 | install:
15 | - pip install --quiet --user proselint
16 | - cp .proselintrc ~
17 | - cd ..
18 | - git clone --depth 1 https://github.com/api-platform/website.git
19 | - cd website
20 | - yarn install --silent
21 | - cd $TRAVIS_BUILD_DIR
22 |
23 | script:
24 | - find . -name '*.md' -exec proselint {} \;
25 | - cd ../website
26 | - bin/retrieve-documentation
27 | - GATSBY_BRANCH_NAME=$TRAVIS_BRANCH yarn gatsby build
28 | # Preserve artifacts
29 | - cd $TRAVIS_BUILD_DIR
30 | - mv ../website/public public
31 |
32 | deploy:
33 | provider: pages
34 | skip_cleanup: true
35 | github_token: $GITHUB_TOKEN
36 | local_dir: public
37 | fqdn: api-platform.com
38 | on:
39 | all_branches: true
40 | condition: $TRAVIS_BRANCH =~ ^(master|2.5|2.4|2.3|2.2|2.1)$
41 |
--------------------------------------------------------------------------------
/admin/schema.org.md:
--------------------------------------------------------------------------------
1 | # Using the Schema.org Vocabulary
2 |
3 | API Platform Admin has native support for the popular [Schema.org](https://schema.org) vocabulary.
4 |
5 | > Schema.org is a collaborative, community activity with a mission to create, maintain, and promote schemas for structured data on the Internet, on web pages, in email messages, and beyond.
6 |
7 | To leverage this capability, your API must use the JSON-LD format and the appropriate Schema.org types.
8 | The following examples will use [API Platform Core](../core/) to create such API, but keep in mind that this feature will work with any JSON-LD API using the Schema.org vocabulary, regardless of the used web framework or programming language.
9 |
10 | ## Displaying Related Resource's Name Instead of its IRI
11 |
12 | By default, IRIs of related objects are displayed in lists and forms.
13 | However, it is often more user-friendly to display a string representation of the resource (such as its name) instead of its ID.
14 |
15 | To configure which property should be shown to represent your entity, map the property containing the name of the object with the `http://schema.org/name` type:
16 |
17 | ```php
18 | // api/src/Entity/Person.php
19 |
20 | /**
21 | * @ApiProperty(iri="http://schema.org/name")
22 | */
23 | private $name;
24 | ```
25 |
26 | ## Emails, URLs and Identifiers
27 |
28 | Besides, it is also possible to use the documentation to customize some fields automatically while configuring the semantics of your data.
29 |
30 | The following Schema.org types are currently supported by API Platform Admin:
31 |
32 | * `http://schema.org/email`: the field will be rendered using the `` React Admin component
33 | * `http://schema.org/url`: the field will be rendered using the `` React Admin component
34 | * `http://schema.org/identifier`: the field will be formatted properly in inputs
35 |
--------------------------------------------------------------------------------
/core/default-order.md:
--------------------------------------------------------------------------------
1 | # Overriding Default Order
2 |
3 | API Platform Core provides an easy way to override the default order of items in your collection.
4 |
5 | By default, items in the collection are ordered in ascending (ASC) order by their resource identifier(s). If you want to
6 | customize this order, you must add an `order` attribute on your ApiResource annotation:
7 |
8 | ```php
9 | HTTP/2 allows a server to pre-emptively send (or "push") responses (along with corresponding "promised" requests) to a client in association with a previous client-initiated request. This can be useful when the server knows the client will need to have those responses available in order to fully process the response to the original request.
4 | >
5 | > —[RFC 7540](https://tools.ietf.org/html/rfc7540#section-8.2)
6 |
7 | API Platform leverages this capability by pushing relations of a resource to clients.
8 |
9 | ```php
10 | import('layouts/MyLayout.vue'),
26 | children: [
27 | ...fooRoutes
28 | ],
29 |
30 | ```
31 |
32 | and store modules in the `src/store/index.js` file.
33 |
34 | ```javascript
35 | // Replace "foo" with the name of the resource type
36 | import foo from '../generated/store/modules/foo/';
37 |
38 | export default function(/* { ssrContext } */) {
39 | const Store = new Vuex.Store({
40 | modules: {
41 | foo,
42 | },
43 |
44 | ```
45 |
46 | Make sure to update the `quasar.conf.js` file with the following:
47 |
48 | ```javascript
49 | framework: {
50 | components: [
51 | 'QTable',
52 | 'QTh',
53 | 'QTr',
54 | 'QTd',
55 | 'QAjaxBar',
56 | 'QBreadcrumbs',
57 | 'QBreadcrumbsEl',
58 | 'QSpace',
59 | 'QInput',
60 | 'QForm',
61 | 'QSelect',
62 | 'QMarkupTable',
63 | 'QDate',
64 | 'QTime',
65 | 'QCheckbox',
66 | 'QPopupProxy'
67 |
68 | ...
69 | ],
70 | directives: [..., 'ClosePopup'],
71 | plugins: ['Notify'],
72 | config: {
73 | ...
74 | notify: {
75 | position: 'top',
76 | multiLine: true,
77 | timeout: 0,
78 | },
79 | },
80 | ```
81 |
--------------------------------------------------------------------------------
/client-generator/vuejs.md:
--------------------------------------------------------------------------------
1 | # Vue.js Generator
2 |
3 | Bootstrap a Vue.js application using [vue-cli](https://github.com/vuejs/vue-cli):
4 |
5 | $ vue init webpack-simple my-app
6 | $ cd my-app
7 |
8 | Install the required dependencies:
9 |
10 | $ yarn add vue-router vuex vuex-map-fields babel-plugin-transform-builtin-extend babel-preset-es2015 babel-preset-stage-2 lodash
11 |
12 | Optionally, install Bootstrap and Font Awesome to get an app that looks good:
13 |
14 | $ yarn add bootstrap font-awesome
15 |
16 | To generate all the code you need for a given resource run the following command:
17 |
18 | $ npx @api-platform/client-generator https://demo.api-platform.com src/ --generator vue --resource book
19 | # Replace the URL with the entrypoint of your Hydra-enabled API
20 | # Omit the resource flag to generate files for all resource types exposed by the API
21 |
22 | The code is ready to be executed! Register the generated routes and store modules in the `main.js` file. Here is an example:
23 |
24 | ```javascript
25 | import Vue from 'vue'
26 | import Vuex from 'vuex';
27 | import VueRouter from 'vue-router';
28 | import App from './App.vue';
29 | import 'bootstrap/dist/css/bootstrap.css';
30 | import 'font-awesome/css/font-awesome.css';
31 | import book from './store/modules/book/';
32 | import bookRoutes from './router/book';
33 |
34 | Vue.config.productionTip = false;
35 |
36 | Vue.use(Vuex);
37 | Vue.use(VueRouter);
38 |
39 | const store = new Vuex.Store({
40 | modules: {
41 | book,
42 | }
43 | });
44 |
45 | const router = new VueRouter({
46 | mode: 'history',
47 | routes: [
48 | ...bookRoutes,
49 | ],
50 | });
51 |
52 | new Vue({
53 | store,
54 | router,
55 | render: h => h(App),
56 | }).$mount('#app');
57 |
58 | ```
59 |
60 | Make sure to update the `.babelrc` file with the following:
61 |
62 | ```javascript
63 | {
64 | "presets": [
65 | ["es2015", { "modules": false }],
66 | ["stage-2"]
67 | ],
68 | "plugins": [
69 | ["babel-plugin-transform-builtin-extend", {
70 | globals: ["Error", "Array"]
71 | }]
72 | ]
73 | }
74 | ```
75 |
76 | Go to `https://localhost/books/` to start using your app.
77 | That's all!
78 |
--------------------------------------------------------------------------------
/admin/index.md:
--------------------------------------------------------------------------------
1 | # The API Platform Admin
2 |
3 | 
4 |
5 | API Platform Admin is a tool to automatically create a beautiful and fully featured administration interface
6 | for any API supporting [the Hydra Core Vocabulary](http://www.hydra-cg.com/) or other API specification formats supported by [`@api-platform/api-doc-parser`](https://github.com/api-platform/api-doc-parser) (experimental support for [OpenAPI](https://www.openapis.org/) is also available).
7 |
8 | API Platform Admin is the perfect companion of APIs created
9 | using [the API Platform framework](https://api-platform.com), but also supports APIs written with any other programming language or framework as long as they expose a standard Hydra API documentation.
10 |
11 | API Platform Admin is a 100% standalone Single-Page-Application with no coupling to the server part,
12 | according to the API-first paradigm.
13 |
14 | API Platform Admin parses the API documentation then uses the awesome [React Admin](https://marmelab.com/react-admin/)
15 | library to expose a nice, responsive, management interface (Create-Retrieve-Update-Delete) for all documented resource types.
16 |
17 | You can **customize everything** by using provided React Admin and [Material UI](https://material-ui.com/) components, or by writing your custom [React](https://reactjs.org/) components.
18 |
19 | ## Features
20 |
21 | * Automatically generates an admin interface for all the resources of the API thanks to hypermedia features of Hydra
22 | * Generates 'list', 'create', 'show', and 'edit' screens, as well as a delete button
23 | * Generates suitable inputs and fields according to the API doc (e.g. number HTML input for numbers, checkbox for booleans, selectbox for relationships...)
24 | * Generates suitable inputs and fields according to Schema.org types if available (e.g. email field for http://schema.org/email)
25 | * Handles relationships
26 | * Supports pagination
27 | * Supports filters and ordering
28 | * Automatically validates whether a field is mandatory client-side according to the API description
29 | * Sends proper HTTP requests to the API and decodes them using Hydra and JSON-LD formats
30 | * Nicely displays server-side errors (e.g. advanced validation)
31 | * **100% customizable**
32 |
--------------------------------------------------------------------------------
/admin/authentication-support.md:
--------------------------------------------------------------------------------
1 | # Authentication Support
2 |
3 | API Platform Admin delegates the authentication support to React Admin.
4 | Refer to [the chapter dedicated to authentication in the React Admin documentation](https://marmelab.com/react-admin/Authentication.html)
5 | for more information.
6 |
7 | In short, you have to tweak data provider and api document parser, like this:
8 |
9 | ```javascript
10 | // admin/src/App.js
11 |
12 | import React from 'react';
13 | import { HydraAdmin } from '@api-platform/admin';
14 | import parseHydraDocumentation from '@api-platform/api-doc-parser/lib/hydra/parseHydraDocumentation';
15 | import { dataProvider as baseDataProvider, fetchHydra as baseFetchHydra } from '@api-platform/admin';
16 | import authProvider from './authProvider';
17 | import { Redirect } from 'react-router-dom';
18 |
19 | const entrypoint = process.env.REACT_APP_API_ENTRYPOINT;
20 | const fetchHeaders = {'Authorization': `Bearer ${window.localStorage.getItem('token')}`};
21 | const fetchHydra = (url, options = {}) => baseFetchHydra(url, {
22 | ...options,
23 | headers: new Headers(fetchHeaders),
24 | });
25 | const apiDocumentationParser = entrypoint => parseHydraDocumentation(entrypoint, { headers: new Headers(fetchHeaders) })
26 | .then(
27 | ({ api }) => ({api}),
28 | (result) => {
29 | switch (result.status) {
30 | case 401:
31 | return Promise.resolve({
32 | api: result.api,
33 | customRoutes: [{
34 | props: {
35 | path: '/',
36 | render: () => ,
37 | },
38 | }],
39 | });
40 |
41 | default:
42 | return Promise.reject(result);
43 | }
44 | },
45 | );
46 | const dataProvider = baseDataProvider(entrypoint, fetchHydra, apiDocumentationParser);
47 |
48 | export default props => (
49 |
55 | );
56 | ```
57 |
--------------------------------------------------------------------------------
/outline.yaml:
--------------------------------------------------------------------------------
1 | chapters:
2 | - title: 'The Distribution: Create Powerful APIs with Ease'
3 | path: distribution
4 | items:
5 | - index
6 | - testing
7 | - debugging
8 | - title: The API Component
9 | path: core
10 | items:
11 | - index
12 | - getting-started
13 | - design
14 | - extending
15 | - testing
16 | - operations
17 | - graphql
18 | - data-providers
19 | - data-persisters
20 | - filters
21 | - subresources
22 | - serialization
23 | - validation
24 | - security
25 | - content-negotiation
26 | - pagination
27 | - deprecations
28 | - default-order
29 | - performance
30 | - extensions
31 | - messenger
32 | - dto
33 | - swagger
34 | - json-schema
35 | - mercure
36 | - push-relations
37 | - errors
38 | - external-vocabularies
39 | - operation-path-naming
40 | - extending-jsonld-context
41 | - identifiers
42 | - mongodb
43 | - elasticsearch
44 | - controllers
45 | - events
46 | - file-upload
47 | - jwt
48 | - form-data
49 | - angularjs-integration
50 | - fosuser-bundle
51 | - nelmio-api-doc
52 | - configuration
53 | - title: The Schema Generator Component
54 | path: schema-generator
55 | items:
56 | - index
57 | - getting-started
58 | - configuration
59 | - title: The Admin Component
60 | path: admin
61 | items:
62 | - index
63 | - getting-started
64 | - customizing
65 | - handling-relations-to-collections
66 | - schema.org
67 | - authentication-support
68 | - title: The Client Generator Component
69 | path: client-generator
70 | items:
71 | - index
72 | - react
73 | - nextjs
74 | - react-native
75 | - vuejs
76 | - vuetify
77 | - quasar
78 | - typescript
79 | - troubleshooting
80 | - title: Deployment
81 | path: deployment
82 | items:
83 | - index
84 | - kubernetes
85 | - docker-compose
86 | - heroku
87 | - traefik
88 | - title: Extra
89 | path: extra
90 | items:
91 | - releases
92 | - philosophy
93 | - troubleshooting
94 | - contribution-guides
95 | - conduct
96 |
--------------------------------------------------------------------------------
/client-generator/troubleshooting.md:
--------------------------------------------------------------------------------
1 | # Troubleshooting
2 |
3 | * The generator does not perform any authentication, so you must ensure that all referenced Hydra paths for your API are
4 | accessible anonymously. If you are using API Platform this will at least include:
5 |
6 | ```
7 | api_entrypoint ANY ANY ANY /{index}.{_format}
8 | api_doc ANY ANY ANY /docs.{_format}
9 | api_jsonld_context ANY ANY ANY /contexts/{shortName}.{_format}
10 | ```
11 |
12 | * If you receive `Error: The class http://www.w3.org/ns/hydra/core#ApiDocumentation doesn't exist.` you may have
13 | specified the documentation URL instead of the entrypoint. For example if you are using API Platform and your
14 | documentation URL is at [https://demo.api-platform.com/docs](https://demo.api-platform.com/docs) the entry point is
15 | likely at [https://demo.api-platform.com](https://demo.api-platform.com). You can see an example of the expected
16 | response from an entrypoint in your browser by visiting
17 | [https://demo.api-platform.com/index.jsonld](https://demo.api-platform.com/index.jsonld).
18 |
19 | * If you receive `TypeError: Cannot read property '@type' of undefined` or `TypeError: Cannot read property '0'
20 | of undefined` check that the URL you specified is accessible and returns jsonld. You can check from the command line
21 | you are using by running something like `curl https://demo.api-platform.com/`.
22 |
23 | * If you receive a message like this:
24 |
25 | ```
26 | { Error
27 | at done (/usr/local/share/.config/yarn/global/node_modules/jsonld/js/jsonld.js:6851:19)
28 | at
29 | at process._tickCallback (internal/process/next_tick.js:188:7)
30 | name: 'jsonld.InvalidUrl',
31 | message: 'Dereferencing a URL did not result in a JSON object. The response was valid JSON, but it was not a JSON object.',
32 | details:
33 | { code: 'invalid remote context',
34 | url: 'https://demo.api-platform.com/contexts/Entrypoint',
35 | cause: null } }
36 | ```
37 |
38 | Check access to the specified URL, in this case `https://demo.api-platform.com/contexts/Entrypoint`, use curl to check
39 | access and the response `curl https://demo.api-platform.com/contexts/Entrypoint`. In the above case an "Access Denied"
40 | message in JSON format was being returned.
41 |
--------------------------------------------------------------------------------
/distribution/debugging.md:
--------------------------------------------------------------------------------
1 | # Debugging
2 |
3 | 
Watch the Debugging API Platform screencast
4 |
5 | ## Xdebug
6 |
7 | The default Docker stack is shipped without a Xdebug stage. It's easy
8 | though to add [Xdebug](https://xdebug.org/) to your project, for development
9 | purposes such as debugging tests or remote API requests.
10 |
11 | ## Add a Development Stage to the Dockerfile
12 |
13 | To avoid deploying API Platform to production with an active Xdebug extension,
14 | it's recommended to add a custom stage to the end of the `api/Dockerfile`.
15 |
16 | ```Dockerfile
17 | # api/Dockerfile
18 | FROM api_platform_php as api_platform_php_dev
19 |
20 | ARG XDEBUG_VERSION=2.7.2
21 | RUN set -eux; \
22 | apk add --no-cache --virtual .build-deps $PHPIZE_DEPS; \
23 | pecl install xdebug-$XDEBUG_VERSION; \
24 | docker-php-ext-enable xdebug; \
25 | apk del .build-deps
26 | ```
27 |
28 | ## Configure Xdebug with Docker Compose Override
29 |
30 | Using an [override](https://docs.docker.com/compose/reference/overview/#specifying-multiple-compose-files) file named
31 | `docker-compose.override.yml` ensures that the production configuration remains untouched.
32 |
33 | As an example, an override could look like this:
34 |
35 | ```yml
36 | version: "3.4"
37 |
38 | services:
39 | php:
40 | build:
41 | target: api_platform_php_dev
42 | environment:
43 | # See https://docs.docker.com/docker-for-mac/networking/#i-want-to-connect-from-a-container-to-a-service-on-the-host
44 | # See https://github.com/docker/for-linux/issues/264
45 | # The `remote_host` below may optionally be replaced with `remote_connect_back`
46 | XDEBUG_CONFIG: >-
47 | remote_enable=1
48 | remote_host=host.docker.internal
49 | remote_connect_back=1
50 | remote_port=9000
51 | idekey=PHPSTORM
52 | # This should correspond to the server declared in PHPStorm `Preferences | Languages & Frameworks | PHP | Servers`
53 | # Then PHPStorm will use the corresponding path mappings
54 | PHP_IDE_CONFIG: serverName=api-platform
55 | ```
56 |
57 | ## Troubleshooting
58 |
59 | Inspect the installation with the following command. The requested Xdebug
60 | version should be displayed in the output.
61 |
62 | ```console
63 | $ docker-compose exec php php --version
64 |
65 | PHP …
66 | with Xdebug v2.7.2 …
67 | ```
68 |
--------------------------------------------------------------------------------
/extra/conduct.md:
--------------------------------------------------------------------------------
1 | # Contributor Code of Conduct
2 |
3 | As contributors and maintainers of this project, and in the interest of
4 | fostering an open and welcoming community, we pledge to respect all people who
5 | contribute through reporting issues, posting feature requests, updating
6 | documentation, submitting pull requests or patches, and other activities.
7 |
8 | We are committed to making participation in this project a harassment-free
9 | experience for everyone, regardless of level of experience, gender, gender
10 | identity and expression, sexual orientation, disability, personal appearance,
11 | body size, ethnic group, ethnicity, age, religion, or nationality.
12 |
13 | Examples of unacceptable behavior by participants include:
14 |
15 | * The use of sexualized language or imagery
16 | * Personal attacks
17 | * Trolling or insulting/derogatory comments
18 | * Public or private harassment
19 | * Publishing other's private information, such as physical or electronic
20 | addresses, without explicit permission
21 | * Other unethical or unprofessional conduct
22 |
23 | Project maintainers have the right and responsibility to remove, edit, or
24 | reject comments, commits, code, wiki edits, issues, and other contributions
25 | that are not aligned to this Code of Conduct, or to ban temporarily or
26 | permanently any contributor for other behaviors that they deem inappropriate,
27 | threatening, offensive, or harmful.
28 |
29 | By adopting this Code of Conduct, project maintainers commit themselves to
30 | fairly and consistently applying these principles to every aspect of managing
31 | this project. Project maintainers who do not follow or enforce the Code of
32 | Conduct may be permanently removed from the project team.
33 |
34 | This Code of Conduct applies both within project spaces and in public spaces
35 | when an individual is representing the project or its community.
36 |
37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
38 | reported by contacting a project maintainer at dunglas@gmail.com. All
39 | complaints will be reviewed and investigated and will result in a response that
40 | is deemed necessary and appropriate to the circumstances. Maintainers are
41 | obligated to maintain confidentiality regarding the reporter of an
42 | incident.
43 |
44 |
45 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
46 | version 1.3.0, available at
47 | [http://contributor-covenant.org/version/1/3/0/][version]
48 |
49 | [homepage]: http://contributor-covenant.org
50 | [version]: http://contributor-covenant.org/version/1/3/0/
51 |
--------------------------------------------------------------------------------
/admin/getting-started.md:
--------------------------------------------------------------------------------
1 | # Getting Started
2 |
3 | ## Installation
4 |
5 | If you use the [API Platform Distribution](../distribution/), API Platform Admin is already installed, you can skip this installation guide.
6 |
7 | Otherwise, all you need to install API Platform Admin is a JavaScript package manager. We recommend [Yarn](https://yarnpkg.com/) ([npm](https://www.npmjs.com/) is also supported).
8 |
9 | If you don't have an existing React Application, create one using [Create React App](https://create-react-app.dev/):
10 |
11 | $ yarn create react-app my-admin
12 |
13 | Go to the directory of your project:
14 |
15 | $ cd my-admin
16 |
17 | Finally, install the `@api-platform/admin` library:
18 |
19 | $ yarn add @api-platform/admin
20 |
21 | ## Creating the Admin
22 |
23 | To initialize API Platform Admin, register it in your application.
24 | For instance, if you used Create React App, replace the content of `src/App.js` by:
25 |
26 | ```javascript
27 | import React from "react";
28 | import { HydraAdmin } from "@api-platform/admin";
29 |
30 | // Replace with your own API entrypoint
31 | // For instance if https://example.com/api/books is the path to the collection of book resources, then the entrypoint is https://example.com/api
32 | export default () => (
33 |
34 | );
35 | ```
36 |
37 | Be sure to make your API send proper [CORS HTTP headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) to allow
38 | the admin's domain to access it.
39 |
40 | To do so, if you use the API Platform Distribution, update the value of the `CORS_ALLOW_ORIGIN` parameter in `api/.env` (it will be set to `^https?://localhost:?[0-9]*$`
41 | by default).
42 |
43 | If you use a custom installation of Symfony and [API Platform Core](../core/), you will need to adjust the [NelmioCorsBundle configuration](https://github.com/nelmio/NelmioCorsBundle#configuration) to expose the `Link` HTTP header and to send proper CORS headers on the route under which the API will be served (`/api` by default).
44 | Here is a sample configuration:
45 |
46 | ```yaml
47 | # config/packages/nelmio_cors.yaml
48 |
49 | nelmio_cors:
50 | paths:
51 | '^/api/':
52 | origin_regex: true
53 | allow_origin: ['^http://localhost:[0-9]+'] # You probably want to change this regex to match your real domain
54 | allow_methods: ['GET', 'OPTIONS', 'POST', 'PUT', 'PATCH', 'DELETE']
55 | allow_headers: ['Content-Type', 'Authorization']
56 | expose_headers: ['Link']
57 | max_age: 3600
58 | ```
59 |
60 | Clear the cache to apply this change:
61 |
62 | $ docker-compose exec php bin/console cache:clear --env=prod
63 |
64 | Your new administration interface is ready! Type `yarn start` to try it!
65 |
66 | Note: if you don't want to hardcode the API URL, you can [use an environment variable](https://create-react-app.dev/docs/adding-custom-environment-variables).
67 |
--------------------------------------------------------------------------------
/core/index.md:
--------------------------------------------------------------------------------
1 | # The API Platform Core Library
2 |
3 | API Platform Core is an easy-to-use and powerful library to create [hypermedia-driven REST APIs](https://en.wikipedia.org/wiki/HATEOAS).
4 | It is a component of the [API Platform framework](https://api-platform.com). It can be used as a standalone or with [the Symfony
5 | framework](https://symfony.com) (recommended).
6 |
7 | It embraces [JSON for Linked Data (JSON-LD)](http://json-ld.org) and [Hydra Core Vocabulary](http://www.hydra-cg.com) web
8 | standards but also supports [HAL](http://stateless.co/hal_specification.html), [Swagger/Open API](https://www.openapis.org/), XML, JSON, CSV and YAML.
9 |
10 | Build a working and fully featured CRUD API in minutes. Leverage the awesome features of the tool to develop complex and
11 | high-performance API-first projects.
12 |
13 | If you are starting a new project, the easiest way to get API Platform up is to install
14 | the [API Platform Distribution](../distribution/index.md).
15 |
16 | 
17 |
18 | ## Features
19 |
20 | Here is the fully featured REST API you'll get in minutes:
21 |
22 | * [Automatic CRUD](operations.md)
23 | * Hypermedia (JSON-LD and HAL)
24 | * Machine-readable documentation of the API in the Hydra and [Swagger/Open API](swagger.md) formats,
25 | guessed from PHPDoc, Serializer, Validator and Doctrine ORM / MongoDB ODM metadata
26 | * Nice human-readable documentation built with Swagger UI (including a sandbox) and/or ReDoc
27 | * [Pagination](pagination.md)
28 | * A bunch of [filters](filters.md)
29 | * [Ordering](default-order.md)
30 | * [Validation](validation.md) using the Symfony Validator Component (with groups support)
31 | * Advanced [authentication and authorization](security.md) rules
32 | * Errors serialization (Hydra and the [RFC 7807](https://tools.ietf.org/html/rfc7807) are supported)
33 | * Advanced [serialization](serialization.md) thanks to the Symfony Serializer Component (groups support, relation embedding, max depth...)
34 | * Automatic routes registration
35 | * Automatic entrypoint generation giving access to all resources
36 | * [JWT](jwt.md) and [OAuth](https://oauth.net/) support
37 | * Files and `\DateTime` and serialization and deserialization
38 |
39 | Everything is fully customizable through a powerful [event system](events.md) and strong OOP.
40 |
41 | This bundle is extensively tested (unit and functional). The [`Fixtures/` directory](https://github.com/api-platform/core/tree/master/tests/Fixtures) contains a working app covering all features of the library.
42 |
43 | ## Screencasts
44 |
45 | 
46 |
47 | The easiest and funniest way to learn how to use API Platform is to watch [the more than 60 screencasts available on SymfonyCasts](https://symfonycasts.com/tracks/rest?cid=apip#api-platform)!
48 |
--------------------------------------------------------------------------------
/extra/philosophy.md:
--------------------------------------------------------------------------------
1 | # API Platform's Philosophy
2 |
3 | In 20 years of PHP, the web changed dramatically and is now evolving faster than ever:
4 |
5 | * Now that [they are indexed by Google](http://searchengineland.com/tested-googlebot-crawls-javascript-heres-learned-220157)
6 | and thanks to awesome frontend technologies such as [AngularJS](http://angularjs.org/) or [React](https://facebook.github.io/react/),
7 | [full-JavaScript](https://en.wikipedia.org/wiki/Single-page_application) **webapps are becoming the standard**.
8 | * [Since 2014 internet users spend more time on their mobile devices than on desktops](http://techcrunch.com/2014/08/21/majority-of-digital-media-consumption-now-takes-place-in-mobile-apps/): having a responsive website is mandatory and **native mobile apps are a must-have**.
9 | * [The semantic web](https://en.wikipedia.org/wiki/Semantic_Web) and **especially [Linked Data](https://en.wikipedia.org/wiki/Linked_data)
10 | is a reality**: with the [Schema.org](http://schema.org/) initiative and new open web standards such as [JSON-LD](http://json-ld.org/),
11 | search engines (among a bunch of other services and software) consume structured and machine-readable data at web scale.
12 | Not exposing such data decrease interoperability and search engine ranking/efficiency (think rich snippets).
13 |
14 | [PHP.net](https://www.php.net), [Symfony](https://symfony.com), [Facebook](http://hhvm.com/) and many others have worked hard
15 | to improve and professionalize the PHP ecosystem. The PHP world has closed the gap with most backend solutions and is often
16 | more innovative than them.
17 |
18 | However in critical areas I've described previously, many things can be improved. Almost all existing solutions are still [designed
19 | and documented](https://symfony.com/doc/current/book/page_creation.html) to create websites the old way: a server generates
20 | then sends plain-old HTML documents to browsers.
21 |
22 | [API Platform](https://api-platform.com) is a set of tools for building modern web projects. It is a framework
23 | for API-first projects built on top of Symfony. Like other modern frameworks such as Zend Framework and Symfony, it's both
24 | a full-stack all-in-one framework and a set of independent PHP components and bundles that can be used separately.
25 |
26 | Many of you will distrust the architecture promoted by the framework but read this tutorial until the end and you will
27 | see how API Platform makes modern development easy and fun again:
28 |
29 | * [Start by **creating a hypermedia REST API**](../distribution/index.md) exposing structured data that can
30 | be understood by any compliant client such as your apps but also search engines (JSON-LD with Schema.org vocabulary).
31 | This API is the central and unique entry point to access and modify data. It also encapsulates the whole business logic.
32 | * [Then **create as many clients as you want using frontend technologies you love**](../client-generator/index.md): an HTML5/JavaScript
33 | webapp querying the API in AJAX (of course) but also a native iOS or Android app, or even a desktop application. Clients
34 | only display data and forms.
35 |
--------------------------------------------------------------------------------
/core/operation-path-naming.md:
--------------------------------------------------------------------------------
1 | # Operation Path Naming
2 |
3 | With API Platform Core, you can configure the default resolver used to generate operation paths.
4 | Pre-registered resolvers are available and can easily be overridden.
5 |
6 | ## Configuration
7 |
8 | There are two pre-registered operation path naming services:
9 |
10 | Service name | Entity name | Path result
11 | ------------------------------------------------------|--------------|----------------
12 | `api_platform.path_segment_name_generator.underscore` | `MyResource` | `/my_resources`
13 | `api_platform.path_segment_name_generator.dash` | `MyResource` | `/my-resources`
14 |
15 | The default resolver is `api_platform.path_segment_name_generator.underscore`.
16 | To change it to the dash resolver, add the following lines to `api/config/packages/api_platform.yaml`:
17 |
18 | ```yaml
19 | # api/config/packages/api_platform.yaml
20 | api_platform:
21 | path_segment_name_generator: api_platform.path_segment_name_generator.dash
22 | ```
23 |
24 | ## Create a Custom Operation Path Resolver
25 |
26 | Let's assume we need URLs without separators (e.g. `api.tld/myresources`)
27 |
28 | ### Defining the Operation Segment Name Generator
29 |
30 | Make sure the custom segment generator implements [`ApiPlatform\Core\Operation\PathSegmentNameGeneratorInterface`](https://github.com/api-platform/core/blob/master/src/Operation/PathSegmentNameGeneratorInterface.php):
31 |
32 | ```php
33 | dashize($name);
53 |
54 | return $name;
55 | }
56 |
57 | private function dashize(string $string): string
58 | {
59 | return strtolower(preg_replace('~(?<=\\w)([A-Z])~', '-$1', $string));
60 | }
61 | }
62 | ```
63 |
64 | Note that `$name` contains a camel case string, by default the resource class name (e.g. `MyResource`).
65 |
66 | ### Registering the Service
67 |
68 | If you haven't disabled the autowiring option, the service will be registered automatically and you have nothing more to
69 | do.
70 | Otherwise, you must register this class as a service like in the following example:
71 |
72 | ```yaml
73 | # api/config/services.yaml
74 | services:
75 | # ...
76 | 'App\PathResolver\NoSeparatorsOperationPathResolver': ~
77 | ```
78 |
79 | ### Configuring It
80 |
81 | ```yaml
82 | # api/config/packages/api_platform.yaml
83 | api_platform:
84 | path_segment_name_generator: 'App\PathResolver\NoSeparatorsOperationPathResolver'
85 | ```
86 |
--------------------------------------------------------------------------------
/core/external-vocabularies.md:
--------------------------------------------------------------------------------
1 | # Using External Vocabularies
2 |
3 | JSON-LD allows to define classes and properties of your API with open vocabularies such as [Schema.org](https://schema.org)
4 | and [Good Relations](http://www.heppnetz.de/projects/goodrelations/).
5 |
6 | API Platform Core provides annotations usable on PHP classes and properties for specifying a related external [IRI](https://en.wikipedia.org/wiki/Internationalized_resource_identifier).
7 |
8 | ```php
9 | {
39 | return (
40 |
41 |
42 | {BookRoutes}
43 |
44 |
45 | );
46 | };
47 |
48 | export default RouterComponent;
49 | ```
50 |
51 | Here is an example of an `App.js` file:
52 |
53 | ```javascript
54 | import React, { Component } from 'react';
55 | import { Provider } from 'react-redux';
56 | import thunk from 'redux-thunk';
57 | import { createStore, applyMiddleware, combineReducers } from 'redux';
58 | import { View } from 'react-native';
59 | import {reducer as form} from 'redux-form';
60 |
61 | // see https://github.com/facebook/react-native/issues/14796
62 | import { Buffer } from 'buffer';
63 | global.Buffer = Buffer;
64 |
65 | // see https://github.com/facebook/react-native/issues/16434
66 | import { URL, URLSearchParams } from 'whatwg-url';
67 | global.URL = URL;
68 | global.URLSearchParams = URLSearchParams;
69 |
70 | // see https://github.com/facebook/react-native/issues/12890
71 | import RNEventSource from 'react-native-event-source';
72 | global.EventSource = RNEventSource;
73 |
74 | // Replace "book" with the name of resource type
75 | import book from './reducers/book';
76 | import Router from './Router';
77 |
78 | export default class App extends Component {
79 | render() {
80 | const store = createStore(combineReducers({
81 | book,
82 | form
83 | }), {}, applyMiddleware(thunk));
84 | return (
85 |
86 |
87 |
88 |
89 |
90 | );
91 | }
92 | }
93 | ```
94 |
95 | The code is ready to be executed!
96 |
97 | $ expo start
98 |
99 | ## Screenshots in iOS Simulator
100 |
101 |  
102 |  
103 |
--------------------------------------------------------------------------------
/core/design.md:
--------------------------------------------------------------------------------
1 | # General Design Considerations
2 |
3 | Since you only need to describe the structure of the data to expose, API Platform is both [a "design-first" and "code-first"](https://swagger.io/blog/api-design/design-first-or-code-first-api-development/)
4 | API framework. However, the "design-first" methodology is strongly recommended: first you design the **public shape** of
5 | API endpoints.
6 |
7 | To do so, you have to write a plain old PHP object representing the input and output of your endpoint. This is the class
8 | that is [marked with the `@ApiResource` annotation](../distribution/index.md).
9 | This class **doesn't have** to be mapped with Doctrine ORM, or any other persistence system. It must be simple (it's usually
10 | just a data structure with no or minimal behaviors) and will be automatically converted to [Hydra](extending-jsonld-context.md),
11 | [OpenAPI](swagger.md) and [GraphQL](graphql.md) documentations or schemas by API Platform (there is a 1-1 mapping
12 | between this class and those docs).
13 |
14 | Then, it's up to the developer to feed API Platform with an hydrated instance of this API resource object by implementing
15 | the [`DataProviderInterface`](data-providers.md). Basically, the data provider will query the persistence system (RDBMS,
16 | document or graph DB, external API...), and must hydrate and return the POPO that has been designed as mentioned above.
17 |
18 | When updating a state (`POST`, `PUT`, `PATCH`, `DELETE` HTTP methods), it's up to the developer to properly persist the
19 | data provided by API Platform's resource object [hydrated by the serializer](serialization.md).
20 | To do so, there is another interface to implement: [`DataPersisterInterface`](data-persisters.md).
21 |
22 | This class will read the API resource object (the one marked with `@ApiResource`) and:
23 |
24 | * persist it directly in the database;
25 | * or hydrate a DTO then trigger a command;
26 | * or populate an event store;
27 | * or persist the data in any other useful way.
28 |
29 | The logic of data persisters is the responsibility of application developers, and is **out of the API Platform's scope**.
30 |
31 | For [Rapid Application Development](https://en.wikipedia.org/wiki/Rapid_application_development), convenience and prototyping,
32 | **if and only if the class marked with `@ApiResource` is also a Doctrine entity**, the developer can use the Doctrine
33 | ORM's data provider and persister implementations shipped with API Platform.
34 |
35 | In this case, the public (`@ApiResource`) and internal (Doctrine entity) data models are shared. Then, API Platform will
36 | be able to query, filter, paginate and persist data automatically.
37 | This approach is super-convenient and efficient, but is probably **not a good idea** for non-[CRUD](https://en.wikipedia.org/wiki/Create,_read,_update_and_delete)
38 | and/or large systems.
39 | Again, it's up to the developers to use, or to not use these built-in data providers/persisters depending on the business logic
40 | they are dealing with.
41 | API Platform makes it easy to create custom data providers and persisters.
42 | It also makes it easy to implement patterns such as [CQS](https://www.martinfowler.com/bliki/CommandQuerySeparation.html)
43 | or [CQRS](https://martinfowler.com/bliki/CQRS.html) thanks to [the Messenger Component integration](messenger.md) and the [DTO support](dto.md).
44 |
45 | Last but not least, to create [Event Sourcing](https://martinfowler.com/eaaDev/EventSourcing.html)-based systems, a convenient
46 | approach is:
47 |
48 | * to persist data in an event store using a Messenger handler or a custom [data persister](data-persisters.md)
49 | * to create projections in standard RDBMS (PostgreSQL, MariaDB...) tables or views
50 | * to map those projections with read-only Doctrine entity classes **and** to mark those classes with `@ApiResource`
51 |
52 | You can then benefit from the built-in Doctrine filters, sorting, pagination, auto-joins and all of [the extension points](extending.md) provided by API Platform.
53 |
--------------------------------------------------------------------------------
/core/extending-jsonld-context.md:
--------------------------------------------------------------------------------
1 | # Extending JSON-LD AND Hydra Contexts
2 |
3 | ## JSON-LD
4 |
5 | 
Watch the JSON-LD screencast
6 |
7 | API Platform Core provides the possibility to extend the JSON-LD context of properties. This allows you to describe JSON-LD-typed
8 | values, inverse properties using the `@reverse` keyword and you can even overwrite the `@id` property this way. Everything you define
9 | within the following annotation will be passed to the context. This provides a generic way to extend the context.
10 |
11 | ```php
12 | 
Watch the Hydra screencast
75 |
76 | It's also possible to replace the Hydra context used documentation generator:
77 |
78 | ```php
79 |
110 |
111 |
112 |
116 |
117 |
118 |
119 |
120 | bar
121 |
122 |
123 |
124 |
125 |
126 | ```
127 |
--------------------------------------------------------------------------------
/admin/handling-relations-to-collections.md:
--------------------------------------------------------------------------------
1 | # Handling Relations to Collections
2 |
3 | API Platform Admin handles `to-one` and `to-many` relations automatically.
4 | Thanks to [the Schema.org support](schema.org.md), it's also easy to display the name of a related object instead of its IRI.
5 |
6 | Let's go one step further thanks to the [customization capabilities](customizing.md) of API Platform Admin by adding autocompletion support to form inputs.
7 |
8 | ## Using an Autocomplete Input for Relations
9 |
10 | Let's consider an API exposing `Person` and `Book` resources linked by a `many-to-many`
11 | relation (through the `authors` property).
12 |
13 | This API uses the following PHP code:
14 |
15 | ```php
16 | authors = new ArrayCollection();
79 | }
80 | }
81 | ```
82 |
83 | Notice the "partial search" [filter](../core/filters.md) on the `name` property of the `Book` resource class.
84 |
85 | Now, let's configure API Platform Admin to enable autocompletion for the relation selector:
86 |
87 | ```javascript
88 | import React from "react";
89 | import {
90 | HydraAdmin,
91 | ResourceGuesser,
92 | CreateGuesser,
93 | EditGuesser,
94 | InputGuesser
95 | } from "@api-platform/admin";
96 | import { ReferenceInput, AutocompleteInput } from "react-admin";
97 |
98 | const ReviewsCreate = props => (
99 |
100 |
101 | ({ title: searchText })}
106 | >
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 | );
115 |
116 | const ReviewsEdit = props => (
117 |
118 |
119 |
120 | ({ title: searchText })}
125 | >
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 | );
134 |
135 | export default () => (
136 |
137 |
142 |
143 | );
144 | ```
145 |
146 | The autocomplete field should now work properly!
147 |
--------------------------------------------------------------------------------
/schema-generator/index.md:
--------------------------------------------------------------------------------
1 | # The Schema Generator
2 |
3 | `schema` is a command line tool part of [the API Platform framework](https://api-platform.com) that instantly generates
4 | a PHP data model from the [Schema.org](http://schema.org) vocabulary.
5 | Browse Schema.org, choose the types and properties you need, run our code generator and you're done! You get
6 | a fully featured PHP data model including:
7 |
8 | * A set of PHP entities with properties, constants (enum values), getters, setters, adders and removers. The class
9 | hierarchy provided by Schema.org will be translated to a PHP class hierarchy with parents as `abstract` classes. The generated
10 | code complies with [PSR](http://www.php-fig.org/) coding standards.
11 | * Full high-quality PHPDoc for classes, properties, constants and methods extracted from Schema.org.
12 | * Doctrine ORM annotation mapping including database columns with type guessing, relations with cardinality guessing, class
13 | inheritance (through the `@AbstractSuperclass` annotation).
14 | * Data validation through [Symfony Validator](http://symfony.com/doc/current/book/validation.html) annotations including
15 | data type validation, enum support (choices) and check for required properties.
16 | * Interfaces and [Doctrine `ResolveTargetEntityListener`](https://www.doctrine-project.org/projects/doctrine-orm/en/current/cookbook/resolve-target-entity-listener.html)
17 | support.
18 | * Custom PHP namespace support.
19 | * List of values provided by Schema.org with [PHP Enum](https://github.com/myclabs/php-enum) classes.
20 |
21 | Bonus:
22 |
23 | * The code generator is fully configurable and extendable: all features can be deactivated (e.g.: the Doctrine mapping generator)
24 | and a custom generator can be added (e.g.: a Doctrine ODM mapping generator).
25 | * The generated code can be used as is in a [Symfony](http://symfony.com) app (but it will work too in a raw PHP project
26 | or any other framework including [Laravel](http://laravel.com) and [Zend Framework](http://framework.zend.com/)).
27 |
28 | ## What Is Schema.org?
29 |
30 | Schema.org is a vocabulary representing common data structures and their relations. Schema.org can be exposed as [JSON-LD](https://en.wikipedia.org/wiki/JSON-LD),
31 | [microdata](https://en.wikipedia.org/wiki/Microdata_(HTML)) and [RDFa](https://en.wikipedia.org/wiki/RDFa).
32 | Extracting semantical data exposed in the Schema.org vocabulary is supported by a growing number of companies including
33 | Google (Search, Gmail), Yahoo!, Bing and Yandex.
34 |
35 | ## Why Use Schema.org Data to Generate a PHP Model?
36 |
37 | ### Don't Reinvent the Wheel
38 |
39 | Data models provided by Schema.org are popular and were proven efficient. They cover a broad spectrum of topics including
40 | creative works, e-commerce, events, medicine, social networking, people, postal addresses, organization data, places or reviews.
41 | Schema.org has its root in a ton of preexisting well designed vocabularies and is
42 | successfully used by more and more websites and applications.
43 |
44 | Pick schemas applicable to your application, generate your PHP model, then customize and specialize it to fit your needs.
45 |
46 | ### Improve SEO and User Experience
47 |
48 | Adding Schema.org markup to websites and apps increases their ranking in search engines results and enables awesome features
49 | such as [Google Rich Snippets](https://support.google.com/webmasters/answer/99170?hl=en) and [Gmail markup](https://developers.google.com/gmail/markup/overview).
50 |
51 | Mapping your app data model to Schema.org structures can be tedious. When using the generator, your data model will be
52 | derived from Schema.org. Adding microdata markup to your templates or serializing your data as JSON-LD will not require
53 | specific mapping nor adaptation. It's a matter of minutes.
54 |
55 | ### Be Ready for The Future
56 |
57 | Schema.org improves the interoperability of your applications. Used with hypermedia technologies such as [Hydra](http://www.hydra-cg.com/)
58 | it's a big step towards the semantic and machine readable web.
59 | It opens the way to generic web API clients able to extract and process data from any website or app using such technologies.
60 |
61 | ## Documentation
62 |
63 | * [Getting Started](getting-started.md)
64 | * [Configuration](configuration.md)
65 |
--------------------------------------------------------------------------------
/core/form-data.md:
--------------------------------------------------------------------------------
1 | # Accept `application/x-www-form-urlencoded` Form Data
2 |
3 | API Platform only supports raw documents as request input (encoded in JSON, XML, YAML...). This has many advantages including support of types and the ability to send back to the API documents originally retrieved through a `GET` request.
4 | However, sometimes - for instance, to support legacy clients - it is necessary to accept inputs encoded in the traditional [`application/x-www-form-urlencoded`](https://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1) format (HTML form content type). This can easily be done using [the powerful event system](events.md) of the framework.
5 |
6 | **⚠ Adding support for `application/x-www-form-urlencoded` makes your API vulnerable to [CSRF attacks](https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)). Be sure to enable proper countermeasures [such as DunglasAngularCsrfBundle](https://github.com/dunglas/DunglasAngularCsrfBundle).**
7 |
8 | In this tutorial, we will decorate the default `DeserializeListener` class to handle form data if applicable, and delegate to the built-in listener for other cases.
9 |
10 | ## Create your `DeserializeListener` Decorator
11 |
12 | This decorator is able to denormalize posted form data to the target object. In case of other format, it fallbacks to the original [DeserializeListener](https://github.com/api-platform/core/blob/91dc2a4d6eeb79ea8dec26b41e800827336beb1a/src/Bridge/Symfony/Bundle/Resources/config/api.xml#L85-L91).
13 |
14 | ```php
15 | denormalizer = $denormalizer;
36 | $this->serializerContextBuilder = $serializerContextBuilder;
37 | $this->decorated = $decorated;
38 | }
39 |
40 | public function onKernelRequest(GetResponseEvent $event): void {
41 | $request = $event->getRequest();
42 | if ($request->isMethodSafe(false) || $request->isMethod(Request::METHOD_DELETE)) {
43 | return;
44 | }
45 |
46 | if ('form' === $request->getContentType()) {
47 | $this->denormalizeFormRequest($request);
48 | } else {
49 | $this->decorated->onKernelRequest($event);
50 | }
51 | }
52 |
53 | private function denormalizeFormRequest(Request $request): void
54 | {
55 | if (!$attributes = RequestAttributesExtractor::extractAttributes($request)) {
56 | return;
57 | }
58 |
59 | $context = $this->serializerContextBuilder->createFromRequest($request, false, $attributes);
60 | $populated = $request->attributes->get('data');
61 | if (null !== $populated) {
62 | $context['object_to_populate'] = $populated;
63 | }
64 |
65 | $data = $request->request->all();
66 | $object = $this->denormalizer->denormalize($data, $attributes['resource_class'], null, $context);
67 | $request->attributes->set('data', $object);
68 | }
69 | }
70 | ```
71 |
72 | ## Creating the Service Definition
73 |
74 | ```yaml
75 | # api/config/services.yaml
76 | services:
77 | # ...
78 | 'App\EventListener\DeserializeListener':
79 | tags:
80 | - { name: 'kernel.event_listener', event: 'kernel.request', method: 'onKernelRequest', priority: 2 }
81 | # Autoconfiguration must be disabled to set a custom priority
82 | autoconfigure: false
83 | decorates: 'api_platform.listener.request.deserialize'
84 | arguments:
85 | $decorated: '@App\EventListener\DeserializeListener.inner'
86 | ```
87 |
--------------------------------------------------------------------------------
/core/identifiers.md:
--------------------------------------------------------------------------------
1 | # Identifiers
2 |
3 | Every item operation has an identifier in its URL. Although this identifier is usually a number, it can also be an `UUID`, a date, or the type of your choice.
4 | To help with your development experience, we introduced an identifier normalization process.
5 |
6 | ## Custom Identifier Normalizer
7 |
8 | > In the following chapter, we're assuming that `App\Uuid` is a project-owned class that manages a time-based UUID.
9 |
10 | Let's say you have the following class, which is identified by a `UUID` type. In this example, `UUID` is not a simple string but an object with many attributes.
11 |
12 | ```php
13 | getTimestamp()
56 | }
57 |
58 | /**
59 | * {@inheritdoc}
60 | */
61 | public function supports(string $resourceClass, string $operationName = null, array $context = []): bool
62 | {
63 | return $resourceClass === Person::class;
64 | }
65 | }
66 | ```
67 |
68 | To cover this use case, we need to `denormalize` the identifier to an instance of our `App\Uuid` class. This case is covered by an identifier denormalizer:
69 |
70 | ```php
71 | getMessage());
89 | }
90 | }
91 |
92 | /**
93 | * {@inheritdoc}
94 | */
95 | public function supportsDenormalization($data, $type, $format = null)
96 | {
97 | return is_a($type, Uuid::class, true);
98 | }
99 | }
100 | ```
101 |
102 | Tag this service as an `api_platform.identifier.denormalizer`:
103 |
104 | ```xml
105 |
106 |
107 |
108 | ```
109 |
110 | ```yaml
111 | services:
112 | App\Identifier\UuidNormalizer:
113 | tags:
114 | - { name: api_platform.identifier.denormalizer }
115 | ```
116 |
117 | Your `PersonDataProvider` will now work as expected!
118 |
119 |
120 | ## Supported Identifiers
121 |
122 | API Platform supports the following identifier types:
123 |
124 | - `scalar` (string, integer)
125 | - `\DateTime` (uses the symfony `DateTimeNormalizer` internally, see [DateTimeIdentifierNormalizer](https://github.com/api-platform/core/blob/master/src/Identifier/Normalizer/DateTimeIdentifierDenormalizer.php))
126 | - `\Ramsey\Uuid\Uuid` (see [UuidNormalizer](https://github.com/api-platform/core/blob/master/src/Bridge/RamseyUuid/Identifier/Normalizer/UuidNormalizer.php))
127 |
--------------------------------------------------------------------------------
/core/errors.md:
--------------------------------------------------------------------------------
1 | # Errors Handling
2 |
3 | API Platform comes with a powerful error system. It handles expected (such as faulty JSON documents sent by the
4 | client or validation errors) as well as unexpected errors (PHP exceptions and errors).
5 | API Platform automatically sends the appropriate HTTP status code to the client: `400` for expected errors, `500` for
6 | unexpected ones. It also provides a description of the error in [the Hydra error format](https://www.hydra-cg.com/spec/latest/core/#description-of-http-status-codes-and-errors)
7 | or in the format described in the [RFC 7807](https://tools.ietf.org/html/rfc7807), depending of the format selected during the [content negotiation](content-negotiation.md).
8 |
9 | ## Converting PHP Exceptions to HTTP Errors
10 |
11 | The framework also allows you configure the HTTP status code sent to the clients when custom exceptions are thrown.
12 |
13 | In the following example, we throw a domain exception from the business layer of the application and
14 | configure API Platform to convert it to a `404 Not Found` error:
15 |
16 | ```php
17 | ['checkProductAvailability', EventPriorities::PRE_VALIDATE],
47 | ];
48 | }
49 |
50 | public function checkProductAvailability(GetResponseForControllerResultEvent $event): void
51 | {
52 | $product = $event->getControllerResult();
53 | if (!$product instanceof Product || !$event->getRequest()->isMethodSafe(false)) {
54 | return;
55 | }
56 |
57 | if (!$product->isPubliclyAvailable()) {
58 | // Using internal codes for a better understanding of what's going on
59 | throw new ProductNotFoundException(sprintf('The product "%s" does not exist.', $product->getId()));
60 | }
61 | }
62 | }
63 | ```
64 |
65 | If you use the standard distribution of API Platform, this event listener will be automatically registered. If you use a
66 | custom installation, [learn how to register listeners](events.md#custom-event-listeners).
67 |
68 | Then, configure the framework to catch `App\Exception\ProductNotFoundException` exceptions and convert them into `404`
69 | errors:
70 |
71 | ```yaml
72 | # config/packages/api_platform.yaml
73 | api_platform:
74 | # ...
75 | exception_to_status:
76 | # The 4 following handlers are registered by default, keep those lines to prevent unexpected side effects
77 | Symfony\Component\Serializer\Exception\ExceptionInterface: 400 # Use a raw status code (recommended)
78 | ApiPlatform\Core\Exception\InvalidArgumentException: !php/const Symfony\Component\HttpFoundation\Response::HTTP_BAD_REQUEST
79 | ApiPlatform\Core\Exception\FilterValidationException: 400
80 | Doctrine\ORM\OptimisticLockException: 409
81 |
82 | # Custom mapping
83 | App\Exception\ProductNotFoundException: 404 # Here is the handler for our custom exception
84 | ```
85 |
86 | Any type of `Exception` can be thrown, API Platform will convert it to a Symfony's `HttpException`. The framework also takes
87 | care of serializing the error description according to the request format. For instance, if the API should respond in JSON-LD,
88 | the error will be returned in this format as well:
89 |
90 | `GET /products/1234`
91 |
92 | ```json
93 | {
94 | "@context": "/contexts/Error",
95 | "@type": "Error",
96 | "hydra:title": "An error occurred",
97 | "hydra:description": "The product \"1234\" does not exist."
98 | }
99 | ```
100 |
--------------------------------------------------------------------------------
/deployment/heroku.md:
--------------------------------------------------------------------------------
1 | # Deploying an API Platform App on Heroku
2 |
3 | [Heroku](https://www.heroku.com) is a popular, fast, scalable and reliable *Platform As A Service* (PaaS). As Heroku offers a
4 | free plan including database support through [Heroku Postgres](https://www.heroku.com/postgres), it's a convenient way
5 | to experiment with API Platform.
6 |
7 | The API Platform Heroku integration also supports MySQL databases provided by [the ClearDB add-on](https://addons.heroku.com/cleardb).
8 |
9 | Deploying API Platform applications on Heroku is straightforward and you will learn how to do it in this tutorial.
10 |
11 | *Note: this tutorial works perfectly well with API Platform but also with any Symfony application based on the Symfony Standard
12 | Edition.*
13 |
14 | If you don't already have one, [create an account on Heroku](https://signup.heroku.com/signup/dc). Then install [the Heroku
15 | toolbelt](https://devcenter.heroku.com/articles/getting-started-with-php#set-up). We're guessing you already
16 | have a working install of [Composer](http://getcomposer.org). Perfect, we will need it.
17 |
18 | Create a new [API Platform project](distribution/index.md) which will be used in the rest of this example.
19 |
20 | Heroku relies on [environment variables](https://devcenter.heroku.com/articles/config-vars) for its configuration. Regardless
21 | of what provider you choose for hosting your application, using environment variables to configure your production environment
22 | is a best practice promoted by API Platform.
23 |
24 | Create a Heroku `app.json` file at the root of the `api/` directory to configure the deployment:
25 |
26 | ```json
27 | {
28 | "success_url": "/",
29 | "env": {
30 | "APP_ENV": "prod",
31 | "APP_SECRET": {"generator": "secret"},
32 | "CORS_ALLOW_ORIGIN": "https://your-client-url.com"
33 | },
34 | "addons": [
35 | "heroku-postgresql"
36 | ],
37 | "buildpacks": [
38 | {
39 | "url": "https://github.com/heroku/heroku-buildpack-php"
40 | }
41 | ],
42 | "scripts": {
43 | "postdeploy": "php bin/console doctrine:schema:create"
44 | }
45 | }
46 | ```
47 |
48 | The file also tells the Heroku deployment system to build a PHP container and to add the Postgres add-on.
49 |
50 | We are almost done, but API Platform (and Symfony) has a particular directory structure which requires further configuration.
51 | We must tell Heroku that the document root is `public/`, and that all other directories must be private.
52 |
53 | Create a new file named `Procfile` in the `api/` directory with the following content:
54 |
55 | ```yaml
56 | web: vendor/bin/heroku-php-apache2 public/
57 | ```
58 |
59 | Be sure to add the Apache Pack to your dependencies:
60 |
61 | composer require symfony/apache-pack
62 |
63 | As Heroku doesn't support Varnish out of the box, let's disable its integration:
64 |
65 | ```diff
66 | # api/config/packages/api_platform.yaml
67 | - http_cache:
68 | - invalidation:
69 | - enabled: true
70 | - varnish_urls: ['%env(VARNISH_URL)%']
71 | - max_age: 0
72 | - shared_max_age: 3600
73 | - vary: ['Content-Type', 'Authorization', 'Origin']
74 | - public: true
75 | ```
76 |
77 | Heroku provides another free service, [Logplex](https://devcenter.heroku.com/articles/logplex), which allows us to centralize
78 | and persist application logs. Because API Platform writes logs on `STDERR`, it will work seamlessly.
79 |
80 | However, if you use Monolog instead of the default logger, you'll need to configure it to output to `STDERR` instead of
81 | in a file.
82 |
83 | Open `api/config/packages/prod/monolog.yaml` and apply the following patch:
84 |
85 | ```diff
86 | handlers:
87 | nested:
88 | type: stream
89 | - path: "%kernel.logs_dir%/%kernel.environment%.log"
90 | + path: php://stderr
91 | level: debug
92 | ```
93 |
94 | We are now ready to deploy our app!
95 |
96 | Go to the `api/` directory, then
97 |
98 | 1. Initialize a git repository:
99 |
100 | git init
101 |
102 | 2. Add all existing files:
103 |
104 | git add --all
105 |
106 | 3. Commit:
107 |
108 | git commit -a -m "My first API Platform app running on Heroku!"
109 |
110 | 4. Create the Heroku application:
111 |
112 | heroku create
113 |
114 | 5. And deploy for the first time:
115 |
116 | git push heroku master
117 |
118 | **We're done.** You can play with the demo API provided with API Platform. It is ready for production and you
119 | can scale it in one click from the Heroku interface.
120 |
121 | To see your logs, run `heroku logs --tail`.
122 |
--------------------------------------------------------------------------------
/core/fosuser-bundle.md:
--------------------------------------------------------------------------------
1 | # FOSUserBundle Integration
2 |
3 | API Platform Core is shipped with a bridge for [FOSUserBundle](https://github.com/FriendsOfSymfony/FOSUserBundle).
4 | If the FOSUser bundle is enabled, this bridge will use its `UserManager` to create, update and delete user resources.
5 |
6 | Note: FOSUserBundle is not well suited for APIs. We strongly encourage you to use the [Doctrine user provider](https://symfony.com/doc/current/security/user_provider.html#entity-user-provider)
7 | shipped with Symfony or to [create a custom user provider](https://symfony.com/doc/current/security/user_provider.html#creating-a-custom-user-provider)
8 | instead of using this bundle.
9 |
10 | 
Watch the User Entity screencast
11 |
12 | ## Installing the Bundle
13 |
14 | The installation procedure of the FOSUserBundle is described [in the main Symfony docs](https://symfony.com/doc/master/bundles/FOSUserBundle/index.html)
15 |
16 | You can:
17 |
18 | * Skip [step 3 (Create your User class)](https://symfony.com/doc/master/bundles/FOSUserBundle/index.html#step-3-create-your-user-class)
19 | and use the class provided in the next paragraph to set up serialization groups the correct way
20 | * Skip [step 4 (Configure your application's security.yml)](https://symfony.com/doc/master/bundles/FOSUserBundle/index.html#step-4-configure-your-application-s-security-yml)
21 | if you are planning to [use a JWT-based authentication using `LexikJWTAuthenticationBundle`](jwt.md)
22 |
23 | If you are using the API Platform Standard Edition, you will need to enable the form services in the symfony framework
24 | configuration options:
25 |
26 | ```yaml
27 | # api/config/packages/framework.yaml
28 | framework:
29 | form: { enabled: true }
30 | ```
31 |
32 | ## Enabling the Bridge
33 |
34 | To enable the provided bridge with FOSUserBundle, you need to add the following configuration to API Platform:
35 | ```yaml
36 | # api/config/packages/api_platform.yaml
37 | api_platform:
38 | enable_fos_user: true
39 | ```
40 |
41 | ## Creating a `User` Entity with Serialization Groups
42 |
43 | Here's an example of declaration of a [Doctrine ORM User class](https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Resources/doc/index.rst#a-doctrine-orm-user-class).
44 | There's also an example for a [Doctrine MongoDB ODM](https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Resources/doc/index.rst#b-mongodb-user-class).
45 | You need to use serialization groups to hide some properties like `plainPassword` (only in read) and `password`. The properties
46 | shown are handled with [`normalization_context`](serialization.md#normalization), while the properties
47 | you can modify are handled with [`denormalization_context`](serialization.md#denormalization).
48 |
49 | Create your User entity with serialization groups:
50 |
51 | ```php
52 | fullname = $fullname;
104 | }
105 |
106 | public function getFullname(): ?string
107 | {
108 | return $this->fullname;
109 | }
110 |
111 | public function isUser(?UserInterface $user = null): bool
112 | {
113 | return $user instanceof self && $user->id === $this->id;
114 | }
115 | }
116 | ```
117 |
--------------------------------------------------------------------------------
/extra/troubleshooting.md:
--------------------------------------------------------------------------------
1 | # Troubleshooting
2 |
3 | This is a list of common pitfalls while using API Platform, and how to avoid them.
4 |
5 | ## Using Docker
6 |
7 | ### With Docker Toolbox on Windows
8 |
9 | Docker Toolbox is not supported anymore by API Platform. Please upgrade to [Docker for Windows](https://www.docker.com/docker-windows).
10 |
11 | ### Error starting userland proxy
12 |
13 | If the `app` container cannot start and display this `Error starting userland proxy: Bind for 0.0.0.0:80`, it means that port 80 is already in use. You can check to see which processes are currently listening on certain ports.
14 |
15 | # Find out if any service listens on port 80.
16 | # You can use this command on UNIX-based OSes like MacOS and Linux.
17 | $ sudo lsof -n -i :80 | grep LISTEN
18 |
19 | # For Windows, you can use netstat.
20 | # This will give you all TCP/IP network connections and not just processes listening to port 80.
21 | $ netstat -a -b
22 |
23 | You can change the port to be used in the `docker-compose.yml` file (default is port 80).
24 |
25 | ## Using API Platform and JMS Serializer in the same project
26 |
27 | For the latest versions of [JMSSerializerBundle](http://jmsyst.com/bundles/JMSSerializerBundle), there is no conflict so everything should work out of the box.
28 |
29 | If you are still using the old, unmaintained v1 of JMSSerializerBundle, the best way would be to [upgrade to v2](https://github.com/schmittjoh/JMSSerializerBundle/blob/2.4.2/UPGRADING.md#upgrading-from-1x-to-20) of JMSSerializerBundle.
30 |
31 | In v1 of JMSSerializerBundle, the `serializer` alias is registered for the JMS Serializer service by default. However, API Platform requires the Symfony Serializer (and not the JMS one) to work properly. If you cannot upgrade for some reason, this behavior can be deactivated using the following configuration:
32 |
33 | ```yaml
34 | # app/config/config.yml
35 |
36 | jms_serializer:
37 | enable_short_alias: false
38 | ```
39 |
40 | The JMS Serializer service is available as `jms_serializer`.
41 |
42 | ## "upstream sent too big header while reading response header from upstream" 502 Error
43 |
44 | Some of your API calls fail with a 502 error and the logs for the api container shows the following error message `upstream sent too big header while reading response header from upstream`.
45 |
46 | This can be due to the cache invalidation headers that are too big for NGINX. When you query the API, API Platform adds the ids of all returned entities and their dependencies in the headers like so : `Cache-Tags: /entity/1,/dependent_entity/1,/entity/2`. This can overflow the default header size (4k) when your API gets larger and more complex.
47 |
48 | You can modify the `api/docker/nginx/conf.d/default.conf` file and set values to `fastcgi_buffer_size` and `fastcgi_buffers` that suit your needs, like so:
49 |
50 | ```
51 | server {
52 | root /srv/api/public;
53 |
54 | location / {
55 | # try to serve file directly, fallback to index.php
56 | try_files $uri /index.php$is_args$args;
57 | }
58 |
59 | location ~ ^/index\.php(/|$) {
60 | # Comment the next line and uncomment the next to enable dynamic resolution (incompatible with Kubernetes)
61 | fastcgi_pass php:9000;
62 | #resolver 127.0.0.11;
63 | #set $upstream_host php;
64 | #fastcgi_pass $upstream_host:9000;
65 |
66 | # Bigger buffer size to handle cache invalidation headers expansion
67 | fastcgi_buffer_size 32k;
68 | fastcgi_buffers 8 16k;
69 |
70 | fastcgi_split_path_info ^(.+\.php)(/.*)$;
71 | include fastcgi_params;
72 | # When you are using symlinks to link the document root to the
73 | # current version of your application, you should pass the real
74 | # application path instead of the path to the symlink to PHP
75 | # FPM.
76 | # Otherwise, PHP's OPcache may not properly detect changes to
77 | # your PHP files (see https://github.com/zendtech/ZendOptimizerPlus/issues/126
78 | # for more information).
79 | fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
80 | fastcgi_param DOCUMENT_ROOT $realpath_root;
81 | # Prevents URIs that include the front controller. This will 404:
82 | # http://domain.tld/index.php/some-path
83 | # Remove the internal directive to allow URIs like this
84 | internal;
85 | }
86 |
87 | # return 404 for all other php files not matching the front controller
88 | # this prevents access to other php files you don't want to be accessible.
89 | location ~ \.php$ {
90 | return 404;
91 | }
92 | }
93 | ```
94 |
95 | You then need to rebuild your containers by running `docker-compose build`.
96 |
--------------------------------------------------------------------------------
/client-generator/react.md:
--------------------------------------------------------------------------------
1 | # React Generator
2 |
3 | 
4 |
5 | The React Client Generator generates a Progressive Web App built with battle-tested libraries from the ecosystem:
6 |
7 | * [React](https://facebook.github.io/react/)
8 | * [Redux](http://redux.js.org)
9 | * [React Router](https://reacttraining.com/react-router/)
10 | * [Redux Form](http://redux-form.com/)
11 |
12 | It is designed to generate code that works seamlessly with [Facebook's Create React App](https://github.com/facebook/create-react-app).
13 |
14 | ## Install
15 |
16 | The easiest way to get started is to install [the API Platform distribution](../distribution/index.md).
17 | It contains the React Client Generator, all dependencies it needs, a Progressive Web App skeleton generated with Create React App,
18 | a development Docker container to serve the webapp, and all the API Platform components you may need, including an API server
19 | supporting Hydra.
20 |
21 | If you use API Platform, jump to the next section!
22 | Alternatively, you can generate a skeleton and install the generator using [npx](https://www.npmjs.com/package/npx).
23 | To use this generator you need [Node.js](https://nodejs.org/) and [Yarn](https://yarnpkg.com/) (or [npm](https://www.npmjs.com/)) installed.
24 |
25 | Bootstrap a React application:
26 |
27 | $ npx create-react-app client
28 | $ cd client
29 |
30 | Install the required dependencies:
31 |
32 | $ yarn add redux react-redux redux-thunk redux-form react-router-dom connected-react-router prop-types lodash
33 |
34 | Optionally, install Bootstrap and Font Awesome to get an app that looks good:
35 |
36 | $ yarn add bootstrap font-awesome
37 |
38 | Finally, start the integrated web server:
39 |
40 | $ yarn start
41 |
42 | ## Generating a Progressive Web App
43 |
44 | If you use the API Platform distribution, generating all the code you need for a given resource is as simple as running the following command:
45 |
46 | $ docker-compose exec client generate-api-platform-client --resource book
47 |
48 | Omit the resource flag to generate files for all resource types exposed by the API.
49 |
50 | If you don't use the standalone installation, run the following command instead:
51 |
52 | $ npx @api-platform/client-generator https://demo.api-platform.com src/ --resource book
53 | # Replace the URL with the entrypoint of your Hydra-enabled API
54 |
55 | The code has been generated, and is ready to be executed!
56 | Register the reducers and the routes in the `client/src/index.js` file:
57 |
58 | ```javascript
59 | import React from 'react';
60 | import ReactDOM from 'react-dom';
61 | import { createStore, combineReducers, applyMiddleware } from 'redux';
62 | import { Provider } from 'react-redux';
63 | import thunk from 'redux-thunk';
64 | import { reducer as form } from 'redux-form';
65 | import { Route, Switch } from 'react-router-dom';
66 | import { createBrowserHistory } from "history";
67 | import {
68 | ConnectedRouter,
69 | connectRouter,
70 | routerMiddleware
71 | } from 'connected-react-router';
72 | import 'bootstrap/dist/css/bootstrap.css';
73 | import 'font-awesome/css/font-awesome.css';
74 | import * as serviceWorker from './serviceWorker';
75 | // Replace "book" with the name of the resource type
76 | import book from './reducers/book/';
77 | import bookRoutes from './routes/book';
78 |
79 | const history = createBrowserHistory();
80 | const store = createStore(
81 | combineReducers({
82 | router: connectRouter(history),
83 | form,
84 | book
85 | /* Replace book with the name of the resource type */
86 | }),
87 | applyMiddleware(routerMiddleware(history), thunk)
88 | );
89 |
90 | ReactDOM.render(
91 |
92 |
93 |
94 | {bookRoutes}
95 | {/* Replace bookRoutes with the name of the resource type */}
96 | Not Found
} />
97 |
98 |
99 | ,
100 | document.getElementById('root')
101 | );
102 |
103 | // If you want your app to work offline and load faster, you can change
104 | // unregister() to register() below. Note this comes with some pitfalls.
105 | // Learn more about service workers: http://bit.ly/CRA-PWA
106 | serviceWorker.unregister();
107 | ```
108 |
109 | Go to `https://localhost/books/` to start using your app.
110 | That's all!
111 |
112 | ## Screenshots
113 |
114 | 
115 | 
116 | 
117 | 
118 | 
119 |
--------------------------------------------------------------------------------
/core/testing.md:
--------------------------------------------------------------------------------
1 | # Testing Utilities
2 |
3 | API Platform Core provides a set of useful utilities dedicated to API testing.
4 | For an overview of how to test an API Platform app, be sure to read [the testing cookbook first](../distribution/testing.md).
5 |
6 | 
Watch the API Tests & Assertions screencast
7 |
8 | ## The Test HttpClient
9 |
10 | API Platform provides its own implementation of the [Symfony HttpClient](https://symfony.com/doc/current/components/http_client.html)'s interfaces, tailored to be used directly in [PHPUnit](https://phpunit.de/) test classes.
11 |
12 | While all the convenient features of Symfony HttpClient are available and usable directly, under the hood the API Platform implementation manipulates [the Symfony HttpKernel](https://symfony.com/doc/current/components/http_kernel.html) directly to simulate HTTP requests and responses.
13 | This approach results in a huge performance boost compared to triggering real network requests.
14 | It also allows access to the [Symfony HttpKernel](https://symfony.com/doc/current/components/http_kernel.html and to all your services via the [Dependency Injection Container](https://symfony.com/doc/current/testing.html#accessing-the-container).
15 | Reuse them to run, for instance, SQL queries or requests to external APIs directly from your tests.
16 |
17 | Install the `symfony/http-client` and `symfony/browser-kit` packages to enabled the API Platform test client:
18 |
19 | $ docker-compose exec php composer require symfony/http-client symfony/browser-kit
20 |
21 | To use the testing client, your test class must extend the `ApiTestCase` class:
22 |
23 | ```php
24 | request('GET', '/books');
36 | // your assertions here...
37 | }
38 | }
39 | ```
40 |
41 | Refer to [the Symfony HttpClient documentation](https://symfony.com/doc/current/components/http_client.html) to discover all the features of the client (custom headers, JSON encoding and decoding, HTTP Basic and Bearer authentication and cookies support, among other things).
42 |
43 |
44 | ## API Test Assertions
45 |
46 | In addition to [the built-in ones](https://phpunit.readthedocs.io/en/latest/assertions.html), API Platform provides convenient PHPUnit assertions dedicated to API testing:
47 |
48 | ```php
49 | request(...);
61 |
62 | // Asserts that the returned JSON is equal to the passed one
63 | $this->assertJsonEquals(/* a JSON document as an array or as a string */);
64 |
65 | // Asserts that the returned JSON is a superset of the passed one
66 | $this->assertJsonContains(/* a JSON document as an array or as a string */);
67 |
68 | // justinrainbow/json-schema must be installed to use the following assertions
69 |
70 | // Asserts that the returned JSON matches the passed JSON Schema
71 | $this->assertMatchesJsonSchema(/* a JSON Schema as an array or as a string */);
72 |
73 | // Asserts that the returned JSON is validated by the JSON Schema generated for this resource by API Platform
74 |
75 | // For collections
76 | $this->assertMatchesResourceCollectionJsonSchema(YourApiResource::class);
77 | // And for items
78 | $this->assertMatchesResourceItemJsonSchema(YourApiResource::class);
79 | }
80 | }
81 | ```
82 |
83 | ## HTTP Test Assertions
84 |
85 | All tests assertions provided by Symfony (assertions for status codes, headers, cookies, XML documents...) can be used out of the box with the API Platform test client:
86 |
87 | ```php
88 | request('GET', '/books');
100 |
101 | $this->assertResponseIsSuccessful();
102 | $this->assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8');
103 | }
104 | }
105 | ```
106 |
107 | [Check out the dedicated Symfony documentation entry](https://symfony.com/doc/current/testing/functional_tests_assertions.html).
108 |
--------------------------------------------------------------------------------
/deployment/kubernetes.md:
--------------------------------------------------------------------------------
1 | # Deploying to a Kubernetes Cluster
2 |
3 | [Kubernetes](https://kubernetes.io/) has become the most popular way to deploy, run and manage containers in production.
4 | Both [Google Cloud Platform](https://cloud.google.com/kubernetes-engine/), [Microsoft Azure](https://azure.microsoft.com/en-us/services/container-service/kubernetes/)
5 | and [Amazon Web Services](https://aws.amazon.com/eks/) provide managed Kubernetes environment.
6 |
7 | [The official API Platform distribution](../distribution/index.md) contains a built-in [Helm](https://helm.sh/) (the k8s
8 | package manager) chart to deploy in a wink on any of these platforms.
9 |
10 | ## Preparing Your Cluster and Your Local Machine
11 |
12 | 1. Create a Kubernetes cluster on your preferred Cloud provider or install Kubernetes locally on your servers
13 | 2. Install [Helm](https://helm.sh/) locally and on your cluster following their documentation
14 | 3. Be sure to be connected to the right Kubernetes container e.g. running: `gcloud config get-value core/project`
15 | 4. Update the Helm repo: `helm repo update`
16 |
17 | ## Creating and Publishing the Docker Images
18 |
19 | 1. Build the PHP and NGINX Docker images:
20 |
21 | docker build -t gcr.io/test-api-platform/php -t gcr.io/test-api-platform/php:latest api --target api_platform_php
22 | docker build -t gcr.io/test-api-platform/nginx -t gcr.io/test-api-platform/nginx:latest api --target api_platform_nginx
23 | docker build -t gcr.io/test-api-platform/varnish -t gcr.io/test-api-platform/varnish:latest api --target api_platform_varnish
24 |
25 | 2. Push your images to your Docker registry, example with [Google Container Registry](https://cloud.google.com/container-registry/):
26 |
27 | Docker client versions <= 18.03:
28 |
29 | gcloud docker -- push gcr.io/test-api-platform/php
30 | gcloud docker -- push gcr.io/test-api-platform/nginx
31 | gcloud docker -- push gcr.io/test-api-platform/varnish
32 |
33 | Docker client versions > 18.03:
34 |
35 | gcloud auth configure-docker
36 | docker push gcr.io/test-api-platform/php
37 | docker push gcr.io/test-api-platform/nginx
38 | docker push gcr.io/test-api-platform/varnish
39 |
40 | ## Deploying
41 |
42 | Firstly you need to update helm dependencies by running:
43 |
44 | helm dependency update ./api/helm/api
45 |
46 | You are now ready to deploy the API!
47 |
48 | Deploy your API to the container:
49 |
50 | helm install ./api/helm/api --namespace=baz --name baz \
51 | --set php.repository=gcr.io/test-api-platform/php \
52 | --set nginx.repository=gcr.io/test-api-platform/nginx \
53 | --set secret=MyAppSecretKey \
54 | --set postgresql.postgresPassword=MyPgPassword \
55 | --set postgresql.persistence.enabled=true \
56 | --set corsAllowOrigin='^https?://[a-z\]*\.mywebsite.com$'
57 |
58 | If you prefer to use a managed DBMS like [Heroku Postgres](https://www.heroku.com/postgres) or
59 | [Google Cloud SQL](https://cloud.google.com/sql/docs/postgres/) (recommended):
60 |
61 | helm install --name api ./api/helm/api \
62 | # ...
63 | --set postgresql.enabled=false \
64 | --set postgresql.url=pgsql://username:password@host/database?serverVersion=9.6
65 |
66 | If you want to use a managed Varnish such as [Fastly](https://www.fastly.com) for the invalidation cache mechanism
67 | provided by API Platform:
68 |
69 | helm install --name api ./api/helm/api \
70 | # ...
71 | --set varnish.enabled=false \
72 | --set varnish.url=https://myvarnish.com
73 |
74 | Finally, build the `client` and `admin` JavaScript apps and [deploy them on a static
75 | website hosting service](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#deployment).
76 |
77 | ## Initializing the Database
78 |
79 | Before running your application for the first time, be sure to create the database schema:
80 |
81 | PHP_POD=$(kubectl --namespace=bar get pods -l app=php -o jsonpath="{.items[0].metadata.name}")
82 | kubectl --namespace=bar exec -it $PHP_POD -- bin/console doctrine:schema:create
83 |
84 | ## Tiller RBAC Issue
85 |
86 | We noticed that some tiller RBAC trouble occurred. You can usually resolve it by running:
87 |
88 | kubectl create serviceaccount --namespace kube-system tiller
89 | serviceaccount "tiller" created
90 |
91 | kubectl create clusterrolebinding tiller-cluster-rule --clusterrole=cluster-admin --serviceaccount=kube-system:tiller
92 | clusterrolebinding "tiller-cluster-rule" created
93 |
94 | kubectl patch deploy --namespace kube-system tiller-deploy -p '{"spec":{"template":{"spec":{"serviceAccount":"tiller"}}}}'
95 | deployment "tiller-deploy" patched
96 |
97 | Please, see the [related issue](https://github.com/kubernetes/helm/issues/3130) for further details / information.
98 | You can also take a look at the [related documentation](https://github.com/kubernetes/helm/blob/master/docs/rbac.md)
99 |
--------------------------------------------------------------------------------
/core/mercure.md:
--------------------------------------------------------------------------------
1 | # Pushing Live Updates Using the Mercure Protocol
2 |
3 | API Platform can automatically send real time updates to the currently connected clients (webapps, mobile apps...) using [the Mercure protocol](https://mercure.rocks).
4 |
5 | > *Mercure* is a protocol allowing to push data updates to web browsers and other HTTP clients in a convenient, fast, reliable and battery-efficient way. It is especially useful to publish real-time updates of resources served through web APIs, to reactive web and mobile apps.
6 | >
7 | > —https://mercure.rocks
8 |
9 | API Platform detects changes made to your Doctrine entities, and sends the updated resources to the Mercure hub.
10 | Then, the Mercure hub dispatches the updates to all connected clients using [Server-sent Events (SSE)](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events).
11 |
12 | 
13 |
14 | ## Installing Mercure Support
15 |
16 | Mercure support is already installed, configured and enabled in [the API Platform distribution](../distribution/index.md).
17 | If you use the distribution, you have nothing more to do, and you can skip to the next section.
18 |
19 | If you have installed API Platform using another method (such as `composer require api`), you need to install a Mercure hub, and the [Symfony MercureBundle](https://github.com/symfony/mercure-bundle):
20 |
21 | First, [download and run a Mercure hub](https://github.com/dunglas/mercure#hub-implementation).
22 | Then, install the Symfony bundle:
23 |
24 | $ composer require mercure
25 |
26 | Finally, 3 environment variables [must be set](https://symfony.com/doc/current/configuration/external_parameters.html):
27 |
28 | * `MERCURE_PUBLISH_URL`: the URL that must be used by API Platform to publish updates to your Mercure hub (can be an internal or a public URL)
29 | * `MERCURE_SUBSCRIBE_URL`: the **public** URL of the Mercure hub that clients will use to subscribe to updates
30 | * `MERCURE_JWT_SECRET`: a valid Mercure [JSON Web Token (JWT)](https://jwt.io/) allowing API Platform to publish updates to the hub
31 |
32 | The JWT **must** contain a `mercure.publish` property containing an array of targets.
33 | This array can be empty to allow publishing anonymous updates only.
34 | [Example publisher JWT](https://jwt.io/#debugger-io?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtZXJjdXJlIjp7InN1YnNjcmliZSI6WyJmb28iLCJiYXIiXSwicHVibGlzaCI6WyJmb28iXX19.LRLvirgONK13JgacQ_VbcjySbVhkSmHy3IznH3tA9PM) (demo key: `!UnsecureChangeMe!`).
35 |
36 | [Learn more about Mercure authorization.](https://github.com/dunglas/mercure/blob/master/spec/mercure.md#authorization)
37 |
38 | ## Pushing the API Updates
39 |
40 | Use the `mercure` attribute to hint API Platform that it must dispatch the updates regarding the given resources to the Mercure hub:
41 |
42 | ```php
43 | i18n.t(key, params)
90 | }
91 | };
92 |
93 | export default new Vuetify(opts);
94 |
95 | The generator comes with a i18n feature to allow quick translations of some labels in the generated code, to make it
96 | work, you need to create the `src/i18n.js` file with the following:
97 | ```
98 | import Vue from 'vue';
99 | import VueI18n from 'vue-i18n';
100 | import messages from './locales/en';
101 | Vue.use(VueI18n);
102 |
103 | export default new VueI18n({
104 | locale: process.env.VUE_APP_I18N_LOCALE || 'en',
105 | fallbackLocale: process.env.VUE_APP_I18N_FALLBACK_LOCALE || 'en',
106 | messages
107 | });
108 | ```
109 |
110 | Update your App.vue with following:
111 | ```
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 | mdi-home
120 |
121 |
122 | Home
123 |
124 |
125 |
126 |
127 | mdi-book
128 |
129 |
130 |
131 | Books
132 |
133 |
134 |
135 |
136 |
137 | mdi-comment-quote
138 |
139 |
140 |
141 | Reviews
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 | Application
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 | © 2019
158 |
159 |
160 |
161 |
162 |
177 | ```
178 |
179 | To finish, update your `main.js` with the following :
180 |
181 | ```javascript
182 | import Vue from 'vue';
183 | import Vuelidate from 'vuelidate';
184 | import App from './App.vue';
185 | import vuetify from './plugins/vuetify';
186 |
187 | import store from './store';
188 | import router from './router';
189 | import i18n from './i18n';
190 |
191 | Vue.config.productionTip = false;
192 |
193 | Vue.use(Vuelidate);
194 |
195 | new Vue({
196 | i18n,
197 | router,
198 | store,
199 | vuetify,
200 | render: h => h(App)
201 | }).$mount('#app');
202 |
203 | ```
204 |
205 | Go to `https://localhost:8000/books/` to start using your app.
206 | That's all!
207 |
--------------------------------------------------------------------------------
/core/extending.md:
--------------------------------------------------------------------------------
1 | # Extending API Platform
2 |
3 | Because it handles the complex, tedious and repetitive task of creating an API infrastructure for you, API Platform lets you focus on what matter the most for the end user: the business logic.
4 | To do so, API Platform provides a lot of extension points you can use to hook your own code.
5 | Those extensions points are taken into account both by the REST and [GraphQL](graphql.md) subsystems.
6 |
7 | The following tables summarizes which extension point to use depending on what you want to do:
8 |
9 | | Extension Point | Usage |
10 | |------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
11 | | [Data Providers](data-providers.md) | adapters for custom persistence layers, virtual fields, custom hydration |
12 | | [Denormalizers](serialization.md) | post-process objects created from the payload sent in the HTTP request body |
13 | | [Voters](security.md#hooking-custom-permission-checks-using-voters) | custom authorization logic |
14 | | [Validation constraints](validation.md) | custom validation logic |
15 | | [Data Persisters](data-persisters) | custom business logic and computations to trigger before, during or after persistence (ex: mail, call to an external API...) |
16 | | [Normalizers](serialization.md#decorating-a-serializer-and-adding-extra-data) | customize the resource sent to the client (add fields in JSON documents, encode codes, dates...) |
17 | | [Filters](filters.md) | create filters for collections and automatically document them (OpenAPI, GraphQL, Hydra) |
18 | | [Serializer Context Builders](serialization.md#changing-the-serialization-context-dynamically) | Changing the Serialization context (e.g. groups) dynamically |
19 | | [Messenger Handlers](messenger.md) | create 100% custom, RPC, async, service-oriented endpoints (should be used in place of custom controllers because the messenger integration is compatible with both REST and GraphQL, while custom controllers only work with REST) |
20 | | [DTOs and Data Transformers](dto.md) | use a specific class to represent the input or output data structure related to an operation |
21 | | [Kernel Events](events.md) | customize the HTTP request or response (REST only, other extension points must be preferred when possible) |
22 |
23 | ## Doctrine Specific Extension Points
24 |
25 | | Extension Point | Usage |
26 | |------------------------------------------------------------|----------------------------------------------------------------------------------------------------|
27 | | [Extensions](extensions.md) | Access to the query builder to change the DQL query |
28 | | [Filters](filters.md#doctrine-orm-and-mongodb-odm-filters) | Add filters documentations (OpenAPI, GraphQL, Hydra) and automatically apply them to the DQL query |
29 |
30 | ## Leveraging the Built-in Infrastructure Using Composition
31 |
32 | While most API Platform classes are marked as `final`, built-in services are straightforward to reuse and customize [using composition](https://en.wikipedia.org/wiki/Composition_over_inheritance).
33 |
34 | For instance, if you want to send a mail after a resource has been persisted, but still want to benefit from the native Doctrine ORM [data persister](data-persisters.md), use [the decorator design pattern](https://en.wikipedia.org/wiki/Decorator_pattern#PHP) to wrap the native data persister in your own class sending the mail.
35 |
36 | To replace existing API Platform services with your decorators, [check out how to decorate services](https://symfony.com/doc/current/service_container/service_decoration.html).
37 |
38 | 
Watch the Service Decoration screencast
39 |
--------------------------------------------------------------------------------
/core/mongodb.md:
--------------------------------------------------------------------------------
1 | # MongoDB Support
2 |
3 | ## Overview
4 |
5 | [MongoDB](https://www.mongodb.com/) is one of the most popular NoSQL document-oriented database, used for its high
6 | write load (useful for analytics or IoT) and high availability (easy to set replica sets with automatic failover). It
7 | can also shard the database easily for horizontal scalability and has a powerful query language for doing aggregation,
8 | text search or geospatial queries.
9 |
10 | API Platform uses [Doctrine MongoDB ODM 2](https://www.doctrine-project.org/projects/mongodb-odm.html) and in particular
11 | its [aggregation builder](https://www.doctrine-project.org/projects/doctrine-mongodb-odm/en/latest/reference/aggregation-builder.html)
12 | to leverage all the possibilities of the database.
13 |
14 | Doctrine MongoDB ODM 2 relies on the [mongodb](https://secure.php.net/manual/en/set.mongodb.php) PHP extension and not on
15 | the legacy [mongo](https://secure.php.net/manual/en/book.mongo.php) extension.
16 |
17 | ## Enabling MongoDB Support
18 |
19 | If the `mongodb` PHP extension is not installed yet, [install it beforehand](https://secure.php.net/manual/en/mongodb.installation.pecl.php).
20 |
21 | If you are using the [API Platform Distribution](../distribution/index.md), modify the `Dockerfile` to add the extension:
22 |
23 | ```diff
24 | # api/Dockerfile
25 | pecl install \
26 | apcu-${APCU_VERSION} \
27 | + mongodb \
28 | ; \
29 | pecl clear-cache; \
30 | docker-php-ext-enable \
31 | apcu \
32 | + mongodb \
33 | opcache \
34 | ; \
35 | ```
36 |
37 | Then rebuild the `php` image:
38 |
39 | $ docker-compose build php
40 |
41 | Add a MongoDB image to the docker-compose file:
42 |
43 | ```yaml
44 | # docker-compose.yml
45 | # ...
46 | db-mongodb:
47 | # In production, you may want to use a managed database service
48 | image: mongo
49 | environment:
50 | - MONGO_INITDB_DATABASE=api
51 | - MONGO_INITDB_ROOT_USERNAME=api-platform
52 | # You should definitely change the password in production
53 | - MONGO_INITDB_ROOT_PASSWORD=!ChangeMe!
54 | volumes:
55 | - db-data:/var/lib/mongodb/data:rw
56 | # You may use a bind-mounted host directory instead, so that it is harder to accidentally remove the volume and lose all your data!
57 | # - ./docker/db/data:/var/lib/mongodb/data:rw
58 | ports:
59 | - "27017:27017"
60 | # ...
61 | ```
62 |
63 | Once the extension is installed, to enable the MongoDB support, require the [Doctrine MongoDB ODM bundle](https://github.com/doctrine/DoctrineMongoDBBundle)
64 | package using Composer:
65 |
66 | $ docker-compose exec php composer req doctrine/mongodb-odm-bundle
67 |
68 | Execute the contrib recipe to have it already configured.
69 |
70 | Change the MongoDB environment variables to match your Docker image:
71 |
72 | ```
73 | # api/.env
74 | MONGODB_URL=mongodb://api-platform:!ChangeMe!@db-mongodb
75 | MONGODB_DB=api
76 | ```
77 |
78 | Change the configuration of API Platform to add the right mapping path:
79 |
80 | ```yaml
81 | # api/config/packages/api_platform.yaml
82 | api_platform:
83 | # ...
84 |
85 | mapping:
86 | paths: ['%kernel.project_dir%/src/Entity', '%kernel.project_dir%/src/Document']
87 |
88 | # ...
89 | ```
90 |
91 | ## Creating Documents
92 |
93 | Creating resources mapped to MongoDB documents is as simple as creating entities:
94 |
95 | ```php
96 | offers = new ArrayCollection();
132 | }
133 |
134 | public function getId(): ?int
135 | {
136 | return $this->id;
137 | }
138 |
139 | public function addOffer(Offer $offer): void
140 | {
141 | $offer->product = $this;
142 | $this->offers->add($offer);
143 | }
144 |
145 | public function removeOffer(Offer $offer): void
146 | {
147 | $offer->product = null;
148 | $this->offers->removeElement($offer);
149 | }
150 |
151 | // ...
152 | }
153 | ```
154 |
155 | ```php
156 | id;
198 | }
199 | }
200 | ```
201 |
202 | Some important information about the mapping:
203 | * Identifier fields always need to be integers with an increment strategy. API Platform does not support the native
204 | [ObjectId](https://docs.mongodb.com/manual/reference/bson-types/#objectid).
205 | * When defining references, always use the id for storing them instead of the native [DBRef](https://docs.mongodb.com/manual/reference/database-references/#dbrefs).
206 | It allows API Platform to manage [filtering on nested properties](filters.md#apifilter-annotation) by using [lookups](https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/).
207 |
208 | ## Filtering
209 |
210 | Doctrine MongoDB ODM filters are practically the same as Doctrine ORM filters.
211 |
212 | See how to use them and how to create custom ones in the [filters documentation](filters.md).
213 |
214 | ## Creating Custom Extensions
215 |
216 | See how to create Doctrine MongoDB ODM custom extensions in the [extensions documentation](extensions.md).
217 |
--------------------------------------------------------------------------------
/core/messenger.md:
--------------------------------------------------------------------------------
1 | # Symfony Messenger Integration: CQRS and Async Message Processing
2 |
3 | API Platform provides an integration with the [Symfony Messenger Component](https://symfony.com/doc/current/messenger.html).
4 |
5 | This feature allows to implement the [Command Query Responsibility Segregation (CQRS)](https://martinfowler.com/bliki/CQRS.html) pattern in a convenient way.
6 | It also makes it easy to send messages through the web API that will be consumed asynchronously.
7 |
8 | Many transports are supported to dispatch messages to async consumers, including RabbitMQ, Apache Kafka, Amazon SQS and Google Pub/Sub.
9 |
10 | ## Installing Symfony Messenger
11 |
12 | To enable the support of Messenger, install the library:
13 |
14 | $ docker-compose exec php composer require messenger
15 |
16 | ## Dispatching a Resource through the Message Bus
17 |
18 | Set the `messenger` attribute to `true`, and API Platform will automatically dispatch the API Resource instance as a message using the message bus provided by the Messenger Component:
19 |
20 | ```php
21 | = 6.5.0.
12 |
13 | ## Enabling Reading Support
14 |
15 | To enable the reading support for Elasticsearch, simply require the Elasticsearch-PHP package using Composer:
16 |
17 | $ composer require elasticsearch/elasticsearch:^6.0
18 |
19 | Then, enable it inside the API Platform configuration:
20 |
21 | ```yaml
22 | # api/config/packages/api_platform.yaml
23 | parameters:
24 | # ...
25 | env(ELASTICSEARCH_HOST): 'http://localhost:9200'
26 |
27 | api_platform:
28 | # ...
29 |
30 | mapping:
31 | paths: ['%kernel.project_dir%/src/Model']
32 |
33 | elasticsearch:
34 | hosts: ['%env(ELASTICSEARCH_HOST)%']
35 |
36 | #...
37 | ```
38 |
39 | ## Creating Models
40 |
41 | API Platform follows the best practices of Elasticsearch:
42 | * a single index per resource should be used because Elasticsearch is going to [drop support for index types and will allow only a single type per
43 | index](https://www.elastic.co/guide/en/elasticsearch/reference/current/removal-of-types.html);
44 | * index name should be the short resource name in lower case;
45 | * the default `_doc` type should be used;
46 | * all fields should be lower case and should use snake case for combining words.
47 |
48 | This involves having mappings and models which absolutely match each other.
49 |
50 | Here is an example of mappings for 2 resources, `User` and `Tweet`, and their models:
51 |
52 | ```json
53 | PUT user
54 | {
55 | "mappings": {
56 | "_doc": {
57 | "properties": {
58 | "id": {
59 | "type": "keyword"
60 | },
61 | "gender": {
62 | "type": "keyword"
63 | },
64 | "age": {
65 | "type": "integer"
66 | },
67 | "first_name": {
68 | "type": "text"
69 | },
70 | "last_name": {
71 | "type": "text"
72 | },
73 | "tweets": {
74 | "type": "nested",
75 | "properties": {
76 | "id": {
77 | "type": "keyword"
78 | },
79 | "date": {
80 | "type": "date",
81 | "format": "yyyy-MM-dd HH:mm:ss"
82 | },
83 | "message": {
84 | "type": "text"
85 | }
86 | },
87 | "dynamic": "strict"
88 | }
89 | },
90 | "dynamic": "strict"
91 | }
92 | }
93 | }
94 | ```
95 |
96 | ```json
97 | PUT tweet
98 | {
99 | "mappings": {
100 | "_doc": {
101 | "properties": {
102 | "id": {
103 | "type": "keyword"
104 | },
105 | "author": {
106 | "properties": {
107 | "id": {
108 | "type": "keyword"
109 | },
110 | "gender": {
111 | "type": "keyword"
112 | },
113 | "age": {
114 | "type": "integer"
115 | },
116 | "first_name": {
117 | "type": "text"
118 | },
119 | "last_name": {
120 | "type": "text"
121 | }
122 | },
123 | "dynamic": "strict"
124 | },
125 | "date": {
126 | "type": "date",
127 | "format": "yyyy-MM-dd HH:mm:ss"
128 | },
129 | "message": {
130 | "type": "text"
131 | }
132 | },
133 | "dynamic": "strict"
134 | }
135 | }
136 | }
137 | ```
138 |
139 | ```php
140 | security = $security;
81 | }
82 |
83 | public function applyToCollection(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, string $operationName = null)
84 | {
85 | $this->addWhere($queryBuilder, $resourceClass);
86 | }
87 |
88 | public function applyToItem(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, array $identifiers, string $operationName = null, array $context = [])
89 | {
90 | $this->addWhere($queryBuilder, $resourceClass);
91 | }
92 |
93 | private function addWhere(QueryBuilder $queryBuilder, string $resourceClass): void
94 | {
95 | if (Offer::class !== $resourceClass || $this->security->isGranted('ROLE_ADMIN') || null === $user = $this->security->getUser()) {
96 | return;
97 | }
98 |
99 | $rootAlias = $queryBuilder->getRootAliases()[0];
100 | $queryBuilder->andWhere(sprintf('%s.user = :current_user', $rootAlias));
101 | $queryBuilder->setParameter('current_user', $user);
102 | }
103 | }
104 |
105 | ```
106 |
107 | Finally, if you're not using the autoconfiguration, you have to register the custom extension with either of those tags:
108 |
109 | ```yaml
110 | # api/config/services.yaml
111 | services:
112 |
113 | # ...
114 |
115 | 'App\Doctrine\CurrentUserExtension':
116 | tags:
117 | - { name: api_platform.doctrine.orm.query_extension.collection }
118 | - { name: api_platform.doctrine.orm.query_extension.item }
119 | ```
120 |
121 | The `api_platform.doctrine.orm.query_extension.collection` tag will register this service as a collection extension.
122 | The `api_platform.doctrine.orm.query_extension.item` does the same thing for items.
123 |
124 | Note that your extensions should have a positive priority if defined. Internal extensions have negative priorities, for reference:
125 |
126 | | Service name | Priority | Class |
127 | |------------------------------------------------------------|------|---------------------------------------------------------|
128 | | api_platform.doctrine.orm.query_extension.eager_loading (collection) | -8 | ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\EagerLoadingExtension |
129 | | api_platform.doctrine.orm.query_extension.eager_loading (item) | -8 | ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\EagerLoadingExtension |
130 | | api_platform.doctrine.orm.query_extension.filter | -16 | ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\FilterExtension |
131 | | api_platform.doctrine.orm.query_extension.filter_eager_loading | -17 | ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\FilterEagerLoadingExtension |
132 | | api_platform.doctrine.orm.query_extension.order | -32 | ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\OrderExtension |
133 | | api_platform.doctrine.orm.query_extension.pagination | -64 | ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\PaginationExtension |
134 |
135 | #### Blocking Anonymous Users
136 |
137 | This example adds a `WHERE` clause condition only when a fully authenticated user without `ROLE_ADMIN` tries to access a resource. It means that anonymous users will be able to access all data. To prevent this potential security issue, the API must ensure that the current user is authenticated.
138 |
139 | To secure the access to endpoints, use the following access control rule:
140 |
141 | ```yaml
142 | # app/config/package/security.yaml
143 | security:
144 | # ...
145 | access_control:
146 | # ...
147 | - { path: ^/offers, roles: IS_AUTHENTICATED_FULLY }
148 | - { path: ^/users, roles: IS_AUTHENTICATED_FULLY }
149 | ```
150 |
151 | ## Custom Doctrine MongoDB ODM Extension
152 |
153 | Creating custom extensions is the same as with Doctrine ORM.
154 |
155 | The interfaces are:
156 | * `ApiPlatform\Core\Bridge\Doctrine\MongoDbOdm\Extension\AggregationItemExtensionInterface` and `ApiPlatform\Core\Bridge\Doctrine\MongoDbOdm\Extension\AggregationCollectionExtensionInterface` to add stages to the [aggregation builder](https://www.doctrine-project.org/projects/doctrine-mongodb-odm/en/latest/reference/aggregation-builder.html).
157 | * `ApiPlatform\Core\Bridge\Doctrine\MongoDbOdm\Extension\AggregationResultItemExtensionInterface` and `ApiPlatform\Core\Bridge\Doctrine\MongoDbOdm\Extension\AggregationResultCollectionExtensionInterface` to return a result.
158 |
159 | The tags are `api_platform.doctrine_mongodb.odm.aggregation_extension.item` and `api_platform.doctrine_mongodb.odm.aggregation_extension.collection`.
160 |
161 | The custom extensions receive the [aggregation builder](https://www.doctrine-project.org/projects/doctrine-mongodb-odm/en/latest/reference/aggregation-builder.html),
162 | used to execute [complex operations on data](https://docs.mongodb.com/manual/aggregation/).
163 |
164 | ## Custom Elasticsearch Extension
165 |
166 | Currently only extensions querying for a collection of items through a [search request](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-body.html)
167 | are supported. So your custom extensions must implement the `RequestBodySearchCollectionExtensionInterface`. Register your
168 | custom extensions as services and tag them with the `api_platform.elasticsearch.request_body_search_extension.collection` tag.
169 |
--------------------------------------------------------------------------------
/core/subresources.md:
--------------------------------------------------------------------------------
1 | # Subresources
2 |
3 | A subresource is a collection or an item that belongs to another resource.
4 | API Platform makes it easy to create such operations.
5 |
6 | 
Watch the Subresources screencast
7 |
8 | The starting point of a subresource must be a relation on an existing resource.
9 | For example, let's create two entities (Question, Answer) and set up a subresource so that `/question/42/answer` gives us
10 | the answer to the question 42:
11 |
12 | ```php
13 | id;
47 | }
48 |
49 | // ...
50 | }
51 | ```
52 |
53 | ```php
54 | id;
91 | }
92 |
93 | // ...
94 | }
95 | ```
96 |
97 | Alternatively, you can use the YAML configuration format:
98 |
99 | ```yaml
100 | # api/config/api_platform/resources.yaml
101 | App\Entity\Answer: ~
102 | App\Entity\Question:
103 | properties:
104 | answer:
105 | subresource:
106 | resourceClass: 'App\Entity\Answer'
107 | collection: false
108 | ```
109 |
110 | Note that all we had to do is to set up `@ApiSubresource` on the `Question::answer` relation. Because the `answer` is a to-one relation, we know that this subresource is an item. Therefore the response will look like this:
111 |
112 | ```json
113 | {
114 | "@context": "/contexts/Answer",
115 | "@id": "/answers/42",
116 | "@type": "Answer",
117 | "id": 42,
118 | "content": "Life, the Universe, and Everything",
119 | "question": "/questions/42"
120 | }
121 | ```
122 |
123 | If you put the subresource on a relation that is to-many, you will retrieve a collection.
124 |
125 | Last but not least, subresources can be nested, such that `/questions/42/answer/comments` will get the collection of comments for the answer to question 42.
126 |
127 | Note: only for `GET` operations are supported at the moment
128 |
129 | ## Using Serialization Groups
130 |
131 | You may want custom groups on subresources, you can set `normalization_context` or `denormalization_context` on that operation. To do so, add a `subresourceOperations` node. For example:
132 |
133 | ```php
134 |
169 |
170 |
171 |
175 |
176 |
177 |
178 | GET
179 |
180 |
181 | foobar
182 |
183 |
184 |
185 |
186 |
187 |
188 | ```
189 |
190 | In the previous examples, the `method` attribute is mandatory, because the operation name doesn't match a supported HTTP
191 | method.
192 |
193 | Note that the operation name, here `api_questions_answer_get_subresource`, is the important keyword.
194 | It'll be automatically set to `$resources_$subresource(s)_get_subresource`. To find the correct operation name you
195 | may use `bin/console debug:router`.
196 |
197 | ## Using Custom Paths
198 |
199 | You can control the path of subresources with the `path` option of the `subresourceOperations` parameter:
200 |
201 | ```php
202 | ` component](https://marmelab.com/react-admin/Resource.html) (and all its appropriate children) for each resource type exposed by a web API.
10 | Under the hood it uses the `@api-platform/api-doc-parser` library to parse the API documentation. The API documentation can use Hydra, OpenAPI and any other format supported by the library.
11 | Resources are listed in the order they appear in the machine-readable documentation.
12 |
13 | However, it's also possible to display only specific resources, and to order them, while still benefiting from all discovery features provided by API Platform Admin.
14 | To cherry-pick the resources to make available through the admin, pass a list of `` components as children of the root component:
15 |
16 | ```javascript
17 | import React from "react";
18 | import { HydraAdmin, ResourceGuesser } from "@api-platform/admin";
19 |
20 | export default () => (
21 |
22 |
23 |
24 |
25 | {/* While deprecated resources are hidden by default, using an explicit ResourceGuesser component allows to add them back. */}
26 |
27 |
28 | );
29 | ```
30 |
31 | Instead of using the `` component provided by API Platform Admin, you can also pass custom React Admin's [`` components](https://marmelab.com/react-admin/Resource.html), or any other React components that are supported by React Admin's [``](https://marmelab.com/react-admin/Admin.html).
32 |
33 | ## Customizing the List View
34 |
35 | The list view can be customized following the same pattern:
36 |
37 | ```javascript
38 | import React from "react";
39 | import {
40 | HydraAdmin,
41 | ResourceGuesser,
42 | ListGuesser,
43 | FieldGuesser
44 | } from "@api-platform/admin";
45 |
46 | const ReviewsList = props => (
47 |
48 |
49 |
50 |
51 | {/* While deprecated fields are hidden by default, using an explicit FieldGuesser component allows to add them back. */}
52 |
53 |
54 | );
55 |
56 | export default () => (
57 |
58 |
59 | {/* ... */}
60 |
61 | );
62 | ```
63 |
64 | In this example, only the fields `author`, `book` and `letter` (that is hidden by default because it is deprecated) will be displayed. The defined order will be respected.
65 |
66 | In addition to the `` component, [all React Admin Fields components](https://marmelab.com/react-admin/Fields.html) can be passed as children of ``.
67 |
68 | ## Customizing the Show View
69 |
70 | For the show view:
71 |
72 | ```javascript
73 | import React from "react";
74 | import {
75 | HydraAdmin,
76 | ResourceGuesser,
77 | ShowGuesser,
78 | FieldGuesser
79 | } from "@api-platform/admin";
80 |
81 | const ReviewsShow = props => (
82 |
83 |
84 |
85 |
86 |
87 | {/* While deprecated fields are hidden by default, using an explicit FieldGuesser component allows to add them back. */}
88 |
89 |
90 |
91 |
92 |
93 | );
94 |
95 | export default () => (
96 |
97 |
98 | {/* ... */}
99 |
100 | );
101 | ```
102 |
103 | In addition to the `` component, [all React Admin Fields components](https://marmelab.com/react-admin/Fields.html) can be passed as children of ``.
104 |
105 | ## Customizing the Create Form
106 |
107 | Again, the same logic applies to forms. Here is how to customize the create form:
108 |
109 | ```javascript
110 | import React from "react";
111 | import {
112 | HydraAdmin,
113 | ResourceGuesser,
114 | CreateGuesser,
115 | InputGuesser
116 | } from "@api-platform/admin";
117 |
118 | const ReviewsCreate = props => (
119 |
120 |
121 |
122 |
123 |
124 | {/* While deprecated fields are hidden by default, using an explicit InputGuesser component allows to add them back. */}
125 |
126 |
127 |
128 |
129 |
130 | );
131 |
132 | export default () => (
133 |
134 |
135 | {/* ... */}
136 |
137 | );
138 | ```
139 |
140 | In addition to the `` component, [all React Admin Input components](https://marmelab.com/react-admin/Inputs.html) can be passed as children of ``.
141 |
142 | For instance, using an autocomplete input is straightforward, [check out the dedicated documentation entry](handling-relations-to-collections.md#using-an-autocomplete-input)!
143 |
144 | ## Customizing the Edit Form
145 |
146 | Finally, you can customize the edit form the same way:
147 |
148 | ```javascript
149 | import React from "react";
150 | import {
151 | HydraAdmin,
152 | ResourceGuesser,
153 | EditGuesser,
154 | InputGuesser
155 | } from "@api-platform/admin";
156 |
157 | const ReviewsEdit = props => (
158 |
159 |
160 |
161 |
162 |
163 | {/* While deprecated fields are hidden by default, using an explicit InputGuesser component allows to add them back. */}
164 |
165 |
166 |
167 |
168 |
169 | );
170 |
171 | export default () => (
172 |
173 |
174 | {/* ... */}
175 |
176 | );
177 | ```
178 |
179 | In addition to the `` component, [all React Admin Input components](https://marmelab.com/react-admin/Inputs.html) can be passed as children of ``.
180 |
181 | For instance, using an autocomplete input is straightforward, [checkout the dedicated documentation entry](handling-relations-to-collections.md#using-an-autocomplete-input)!
182 |
183 | ## Going Further
184 |
185 | API Platform is built on top of [React Admin](https://marmelab.com/react-admin/).
186 | You can use all the features provided by the underlying library with API Platform Admin, including support for [file upload](https://marmelab.com/react-admin/DataProviders.html#decorating-your-data-provider-example-of-file-upload), [authentication](https://marmelab.com/react-admin/Authentication.html), [authorization](https://marmelab.com/react-admin/Authorization.html) and deeper customization.
187 |
188 | To learn more about these capabilities, refer to [the React Admin documentation](https://marmelab.com/react-admin/).
189 |
--------------------------------------------------------------------------------
/core/configuration.md:
--------------------------------------------------------------------------------
1 | # Configuration
2 |
3 | Here's the complete configuration of the Symfony bundle including default values:
4 |
5 | ```yaml
6 | # api/config/packages/api_platform.yaml
7 | api_platform:
8 |
9 | # The title of the API.
10 | title: 'API title'
11 |
12 | # The description of the API.
13 | description: 'API description'
14 |
15 | # The version of the API.
16 | version: '0.0.0'
17 |
18 | # Set this to false if you want Webby to disappear.
19 | show_webby: true
20 |
21 | # Specify a name converter to use.
22 | name_converter: ~
23 |
24 | # Specify a path name generator to use.
25 | path_segment_name_generator: 'api_platform.path_segment_name_generator.underscore'
26 |
27 | # Allow using plain IDs for JSON format
28 | allow_plain_identifiers: false
29 |
30 | doctrine:
31 | # To enable or disable Doctrine ORM support.
32 | enabled: true
33 |
34 | doctrine_mongodb_odm:
35 | # To enable or disable Doctrine MongoDB ODM support.
36 | enabled: false
37 |
38 | eager_loading:
39 | # To enable or disable eager loading.
40 | enabled: true
41 |
42 | # Fetch only partial data according to serialization groups.
43 | # If enabled, Doctrine ORM entities will not work as expected if any of the other fields are used.
44 | fetch_partial: false
45 |
46 | # Max number of joined relations before EagerLoading throws a RuntimeException.
47 | max_joins: 30
48 |
49 | # Force join on every relation.
50 | # If disabled, it will only join relations having the EAGER fetch mode.
51 | force_eager: true
52 |
53 | # Enable the FOSUserBundle integration.
54 | enable_fos_user: false
55 |
56 | # Enable the Nelmio Api doc integration.
57 | enable_nelmio_api_doc: false
58 |
59 | # Enable the Swagger documentation and export.
60 | enable_swagger: true
61 |
62 | # Enable Swagger ui.
63 | enable_swagger_ui: true
64 |
65 | # Enable ReDoc.
66 | enable_re_doc: true
67 |
68 | # Enable the entrypoint.
69 | enable_entrypoint: true
70 |
71 | # Enable the docs.
72 | enable_docs: true
73 |
74 | # Enable the data collector and the WebProfilerBundle integration.
75 | enable_profiler: true
76 |
77 | oauth:
78 | # To enable or disable oauth.
79 | enabled: false
80 |
81 | # The oauth client id.
82 | clientId: ''
83 |
84 | # The oauth client secret.
85 | clientSecret: ''
86 |
87 | # The oauth type.
88 | type: 'oauth2'
89 |
90 | # The oauth flow grant type.
91 | flow: 'application'
92 |
93 | # The oauth token url.
94 | tokenUrl: '/oauth/v2/token'
95 |
96 | # The oauth authentication url.
97 | authorizationUrl: '/oauth/v2/auth'
98 |
99 | # The oauth scopes.
100 | scopes: []
101 |
102 | graphql:
103 | enabled: false
104 | # The default IDE (graphiql or graphql-playground) used when going to the GraphQL endpoint. False to disable.
105 | default_ide: 'graphiql'
106 | graphiql:
107 | enabled: true
108 | graphql_playground:
109 | enabled: true
110 | collection:
111 | pagination:
112 | enabled: true
113 | # The nesting separator used in the filter names.
114 | nesting_separator: _
115 |
116 | elasticsearch:
117 | # To enable or disable Elasticsearch support.
118 | enabled: false
119 |
120 | # The hosts to the Elasticsearch nodes.
121 | hosts: []
122 |
123 | # The mapping between resource classes and indexes.
124 | mapping: []
125 |
126 | swagger:
127 | # The active versions of OpenAPI to be exported or used in the swagger_ui. The first value is the default.
128 | versions: [2, 3]
129 |
130 | # The swagger api keys.
131 | api_keys: []
132 |
133 | collection:
134 | # The name of the query parameter to filter nullable results (with the ExistsFilter).
135 | exists_parameter_name: 'exists'
136 |
137 | # The default order of results.
138 | order: 'ASC'
139 |
140 | # The name of the query parameter to order results (with the OrderFilter).
141 | order_parameter_name: 'order'
142 |
143 | pagination:
144 | # To enable or disable pagination for all resource collections by default.
145 | enabled: true
146 |
147 | # To allow the client to enable or disable the pagination.
148 | client_enabled: false
149 |
150 | # To allow the client to set the number of items per page.
151 | client_items_per_page: false
152 |
153 | # The default number of items per page.
154 | items_per_page: 30
155 |
156 | # The maximum number of items per page.
157 | maximum_items_per_page: 10
158 |
159 | # The default name of the parameter handling the page number.
160 | page_parameter_name: 'page'
161 |
162 | # The name of the query parameter to enable or disable pagination.
163 | enabled_parameter_name: 'pagination'
164 |
165 | # The name of the query parameter to set the number of items per page.
166 | items_per_page_parameter_name: 'itemsPerPage'
167 |
168 | # To allow partial pagination for all resource collections.
169 | # This improves performances by skipping the `COUNT` query.
170 | partial: false
171 |
172 | # To allow the client to enable or disable the partial pagination.
173 | client_partial: false
174 |
175 | # The name of the query parameter to enable or disable the partial pagination.
176 | partial_parameter_name: 'partial' # Default value
177 |
178 | mapping:
179 | # The list of paths with files or directories where the bundle will look for additional resource files.
180 | paths: []
181 |
182 | # The list of your resources class directories. Defaults to the directories of the mapping paths but might differ.
183 | resource_class_directories:
184 | - '%kernel.project_dir%/src/Entity'
185 |
186 | http_cache:
187 | # Automatically generate etags for API responses.
188 | etag: true
189 |
190 | # Default value for the response max age.
191 | max_age: 3600
192 |
193 | # Default value for the response shared (proxy) max age.
194 | shared_max_age: 3600
195 |
196 | # Default values of the "Vary" HTTP header.
197 | vary: ['Accept']
198 |
199 | # To make all responses public by default.
200 | public: ~
201 |
202 | invalidation:
203 | # To enable the tags-based cache invalidation system.
204 | enabled: false
205 |
206 | # URLs of the Varnish servers to purge using cache tags when a resource is updated.
207 | varnish_urls: []
208 |
209 | # To pass options to the client charged with the request.
210 | request_options: []
211 |
212 | # The list of exceptions mapped to their HTTP status code.
213 | exception_to_status:
214 | # With a status code.
215 | Symfony\Component\Serializer\Exception\ExceptionInterface: 400
216 |
217 | # Or with a constant defined in the 'Symfony\Component\HttpFoundation\Response' class.
218 | ApiPlatform\Core\Exception\InvalidArgumentException: !php/const Symfony\Component\HttpFoundation\Response::HTTP_BAD_REQUEST
219 |
220 | # ...
221 |
222 | # The list of enabled formats. The first one will be the default.
223 | formats:
224 | jsonld:
225 | mime_types: ['application/ld+json']
226 |
227 | json:
228 | mime_types: ['application/json']
229 |
230 | html:
231 | mime_types: ['text/html']
232 |
233 | # ...
234 |
235 | # The list of enabled error formats. The first one will be the default.
236 | error_formats:
237 | jsonproblem:
238 | mime_types: ['application/problem+json']
239 |
240 | jsonld:
241 | mime_types: ['application/ld+json']
242 |
243 | # ...
244 | ```
245 |
--------------------------------------------------------------------------------
/core/events.md:
--------------------------------------------------------------------------------
1 | # The Event System
2 |
3 | Note: using Kernel event with API Platform should be mostly limited to tweaking the generated HTTP response. Also, GraphQL is **not supported**.
4 | [For most use cases, better extension points, working both with REST and GraphQL, are available](extending.md).
5 |
6 | API Platform Core implements the [Action-Domain-Responder](https://github.com/pmjones/adr) pattern. This implementation
7 | is covered in depth in the [Creating custom operations and controllers](operations.md#creating-custom-operations-and-controllers)
8 | chapter.
9 |
10 | Basically, API Platform Core executes an action class that will return an entity or a collection of entities. Then a series
11 | of event listeners are executed which validate the data, persist it in database, serialize it (typically in a JSON-LD document)
12 | and create an HTTP response that will be sent to the client.
13 |
14 | To do so, API Platform Core leverages [events triggered by the Symfony HTTP Kernel](https://symfony.com/doc/current/reference/events.html#kernel-events).
15 | You can also hook your own code to those events. There are handy and powerful extension points available at all points
16 | of the request lifecycle.
17 |
18 | If you are using Doctrine, lifecycle events ([ORM](https://www.doctrine-project.org/projects/doctrine-orm/en/current/reference/events.html#lifecycle-events), [MongoDB ODM](https://www.doctrine-project.org/projects/doctrine-mongodb-odm/en/latest/reference/events.html#lifecycle-events))
19 | are also available if you want to hook into the persistence layer's object lifecycle.
20 |
21 | ## Built-in Event Listeners
22 |
23 | These built-in event listeners are registered for routes managed by API Platform:
24 |
25 | Name | Event | [Pre & Post hooks](#custom-event-listeners) | Priority | Description
26 | ------------------------------|--------------------|---------------------------------------------|----------|-------------
27 | `AddFormatListener` | `kernel.request` | None | 7 | Guesses the best response format ([content negotiation](content-negotiation.md))
28 | `ReadListener` | `kernel.request` | `PRE_READ`, `POST_READ` | 4 | Retrieves data from the persistence system using the [data providers](data-providers.md) (`GET`, `PUT`, `DELETE`)
29 | `DeserializeListener` | `kernel.request` | `PRE_DESERIALIZE`, `POST_DESERIALIZE` | 2 | Deserializes data into a PHP entity (`GET`, `POST`, `DELETE`); updates the entity retrieved using the data provider (`PUT`)
30 | `DenyAccessListener` | `kernel.request` | None | 1 | Enforces [access control](security.md) using Security expressions
31 | `ValidateListener` | `kernel.view` | `PRE_VALIDATE`, `POST_VALIDATE` | 64 | [Validates data](validation.md) (`POST`, `PUT`)
32 | `WriteListener` | `kernel.view` | `PRE_WRITE`, `POST_WRITE` | 32 | Persists changes in the persistence system using the [data persisters](data-persisters.md) (`POST`, `PUT`, `DELETE`)
33 | `SerializeListener` | `kernel.view` | `PRE_SERIALIZE`, `POST_SERIALIZE` | 16 | Serializes the PHP entity in string [according to the request format](content-negotiation.md)
34 | `RespondListener` | `kernel.view` | `PRE_RESPOND`, `POST_RESPOND` | 8 | Transforms serialized to a `Symfony\Component\HttpFoundation\Response` instance
35 | `AddLinkHeaderListener` | `kernel.response` | None | 0 | Adds a `Link` HTTP header pointing to the Hydra documentation
36 | `ValidationExceptionListener` | `kernel.exception` | None | 0 | Serializes validation exceptions in the Hydra format
37 | `ExceptionListener` | `kernel.exception` | None | -96 | Serializes PHP exceptions in the Hydra format (including the stack trace in debug mode)
38 |
39 | Some of these built-in listeners can be enabled/disabled by setting operation attributes:
40 |
41 | Attribute | Type | Default | Description
42 | --------------|--------|---------|-------------
43 | `read` | `bool` | `true` | Enables or disables `ReadListener`
44 | `deserialize` | `bool` | `true` | Enables or disables `DeserializeListener`
45 | `validate` | `bool` | `true` | Enables or disables `ValidateListener`
46 | `write` | `bool` | `true` | Enables or disables `WriteListener`
47 | `serialize` | `bool` | `true` | Enables or disables `SerializeListener`
48 |
49 | Some of these built-in listeners can be enabled/disabled by setting request attributes (for instance in the [`defaults`
50 | attribute of an operation](operations.md#recommended-method)):
51 |
52 | Attribute | Type | Default | Description
53 | ---------------|--------|---------|-------------
54 | `_api_receive` | `bool` | `true` | Enables or disables `ReadListener`, `DeserializeListener`, `ValidateListener`
55 | `_api_respond` | `bool` | `true` | Enables or disables `SerializeListener`, `RespondListener`
56 | `_api_persist` | `bool` | `true` | Enables or disables `WriteListener`
57 |
58 | ## Custom Event Listeners
59 |
60 | Registering your own event listeners to add extra logic is convenient.
61 |
62 | The [`ApiPlatform\Core\EventListener\EventPriorities`](https://github.com/api-platform/core/blob/master/src/EventListener/EventPriorities.php) class comes with a convenient set of class constants corresponding to commonly used priorities:
63 |
64 | Constant | Event | Priority |
65 | -------------------|-------------------|----------|
66 | `PRE_READ` | `kernel.request` | 5 |
67 | `POST_READ` | `kernel.request` | 3 |
68 | `PRE_DESERIALIZE` | `kernel.request` | 3 |
69 | `POST_DESERIALIZE` | `kernel.request` | 1 |
70 | `PRE_VALIDATE` | `kernel.view` | 65 |
71 | `POST_VALIDATE` | `kernel.view` | 63 |
72 | `PRE_WRITE` | `kernel.view` | 33 |
73 | `POST_WRITE` | `kernel.view` | 31 |
74 | `PRE_SERIALIZE` | `kernel.view` | 17 |
75 | `POST_SERIALIZE` | `kernel.view` | 15 |
76 | `PRE_RESPOND` | `kernel.view` | 9 |
77 | `POST_RESPOND` | `kernel.response` | 0 |
78 |
79 | In the following example, we will send a mail each time a new book is created using the API:
80 |
81 | ```php
82 | mailer = $mailer;
101 | }
102 |
103 | public static function getSubscribedEvents()
104 | {
105 | return [
106 | KernelEvents::VIEW => ['sendMail', EventPriorities::POST_WRITE],
107 | ];
108 | }
109 |
110 | public function sendMail(ViewEvent $event)
111 | {
112 | $book = $event->getControllerResult();
113 | $method = $event->getRequest()->getMethod();
114 |
115 | if (!$book instanceof Book || Request::METHOD_POST !== $method) {
116 | return;
117 | }
118 |
119 | $message = (new \Swift_Message('A new book has been added'))
120 | ->setFrom('system@example.com')
121 | ->setTo('contact@les-tilleuls.coop')
122 | ->setBody(sprintf('The book #%d has been added.', $book->getId()));
123 |
124 | $this->mailer->send($message);
125 | }
126 | }
127 | ```
128 |
129 | If you use the official API Platform distribution, creating the previous class is enough. The Symfony DependencyInjection
130 | component will automatically register this subscriber as a service and will inject its dependencies thanks to the [autowiring feature](https://symfony.com/doc/current/service_container/autowiring.html).
131 |
132 | Alternatively, [the subscriber must be registered manually](https://symfony.com/doc/current/components/event_dispatcher.html#connecting-listeners).
133 |
--------------------------------------------------------------------------------
/deployment/docker-compose.md:
--------------------------------------------------------------------------------
1 | # Deploying with Docker Compose
2 |
3 | While [Docker Compose](https://docs.docker.com/compose/) is mainly known and used in a development environment, it [can
4 | actually be used in production too](https://docs.docker.com/compose/production/). This is especially suitable for prototyping
5 | or small-scale deployments, where the robustness (and the associated complexity) of [Kubernetes](kubernetes.md) is not
6 | required.
7 |
8 | It is recommended that you build the Docker images in a [CI (continuous integration)](https://en.wikipedia.org/wiki/Continuous_integration)
9 | job, or failing which, on your development machine. The built images should then be pushed to a [container registry](https://docs.docker.com/registry/introduction/),
10 | e.g. [Docker Hub](https://hub.docker.com/), [Google Container Registry](https://cloud.google.com/container-registry/),
11 | [GitLab Container Registry](https://docs.gitlab.com/ee/user/packages/container_registry/). On the production server, you
12 | would pull the pre-built images from the container registry. This maintains a separation of concerns between the build
13 | environment and the production environment.
14 |
15 | ## Installing the Docker Compose Setup for Production
16 |
17 | If you are using the [API Platform Distribution](../distribution/index.md), we provide a [ready-to-deploy Docker Compose
18 | setup for production](https://github.com/api-platform/docker-compose-prod), with (optional) [Let's Encrypt](https://letsencrypt.org/)
19 | integration.
20 |
21 | It is designed to be used as a companion to the distribution, and as such it needs to be placed inside a subdirectory at
22 | the top level of the distribution project:
23 |
24 | $ wget -O - https://github.com/api-platform/docker-compose-prod/archive/master.tar.gz | tar -xzf - && mv docker-compose-prod-master docker-compose-prod
25 | $ git add docker-compose-prod
26 |
27 | Then commit the changes to your git repository. The `docker-compose-prod` directory should be checked in.
28 |
29 | ## Building and Pushing the Docker Images
30 |
31 | These steps should be performed in a CI job (recommended) or on your development machine.
32 |
33 | 1. Make sure the environment variables required for the build are set.
34 |
35 | If you are building the images in a CI job, these environment variables should be set as part of your CI job's environment.
36 |
37 | If you are building on your development machine, you could set the environment variables in the `.env` file at the
38 | top level of the distribution project (not to be confused with `api/.env` which is used by the Symfony application).
39 | For example:
40 |
41 | ```
42 | ADMIN_IMAGE=registry.example.com/api-platform/admin
43 | CLIENT_IMAGE=registry.example.com/api-platform/client
44 | NGINX_IMAGE=registry.example.com/api-platform/nginx
45 | PHP_IMAGE=registry.example.com/api-platform/php
46 | REACT_APP_API_ENTRYPOINT=https://api.example.com
47 | VARNISH_IMAGE=registry.example.com/api-platform/varnish
48 | ```
49 |
50 | **Note**: `REACT_APP_API_ENTRYPOINT` must be an exact match of the target domain name where your API will be accessed
51 | from, since its value is [embedded during build time](https://create-react-app.dev/docs/adding-custom-environment-variables).
52 | See [this discussion for possible workarounds](https://github.com/facebook/create-react-app/issues/2353) if this limitation
53 | is unacceptable for your project.
54 |
55 | 2. Build the Docker images:
56 |
57 | $ docker-compose -f docker-compose-prod/docker-compose.build.yml pull --ignore-pull-failures
58 | $ docker-compose -f docker-compose-prod/docker-compose.build.yml build --pull
59 |
60 | 3. Push the built images to the container registry:
61 |
62 | $ docker-compose -f docker-compose-prod/docker-compose.build.yml push
63 |
64 | ## Pulling the Docker Images and Running the Services
65 |
66 | These steps should be performed on the production server.
67 |
68 | 1. Make sure the environment variables required are set.
69 |
70 | You could set the environment variables in the `.env` file at the top level of the distribution project (not to be
71 | confused with `api/.env` which is used by the Symfony application). For example:
72 |
73 | ```
74 | ADMIN_HOST=admin.example.com
75 | ADMIN_IMAGE=registry.example.com/api-platform/admin
76 | API_HOST=api.example.com
77 | APP_SECRET=3c857494cfcc42c700dfb7a6
78 | CLIENT_HOST=example.com,www.example.com
79 | CLIENT_IMAGE=registry.example.com/api-platform/client
80 | CORS_ALLOW_ORIGIN=^https://(?:\w+\.)?example\.com$
81 | DATABASE_URL=postgres://api-platform:4e3bc2766fe81df300d56481@db/api
82 | MERCURE_ALLOW_ANONYMOUS=0
83 | MERCURE_CORS_ALLOWED_ORIGINS=https://example.com,https://admin.example.com
84 | MERCURE_HOST=mercure.example.com
85 | MERCURE_JWT_KEY=4121344212538417de3e2118
86 | MERCURE_JWT_SECRET=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtZXJjdXJlIjp7InN1YnNjcmliZSI6WyJmb28iLCJiYXIiXSwicHVibGlzaCI6WyJmb28iXX19.B0MuTRMPLrut4Nt3wxVvLtfWB_y189VEpWMlSmIQABQ
87 | MERCURE_SUBSCRIBE_URL=https://mercure.example.com/hub
88 | NGINX_IMAGE=registry.example.com/api-platform/nginx
89 | PHP_IMAGE=registry.example.com/api-platform/php
90 | POSTGRES_PASSWORD=4e3bc2766fe81df300d56481
91 | REACT_APP_API_ENTRYPOINT=https://api.example.com
92 | TRUSTED_HOSTS=^(?:localhost|api|api\.example\.com)$
93 | VARNISH_IMAGE=registry.example.com/api-platform/varnish
94 | ```
95 |
96 | **Important**: Please make sure to change all the passwords, keys and secret values to your own.
97 |
98 | 2. Set up a redirect from e.g. `www.example.com` to `example.com`:
99 |
100 | $ mkdir -p docker-compose-prod/docker/nginx-proxy/vhost.d
101 | $ echo 'return 301 https://example.com$request_uri;' > docker-compose-prod/docker/nginx-proxy/vhost.d/www.example.com
102 |
103 | **Note**: If you do not want such a redirect, or you want it to be the other way round, please adapt to suit your needs.
104 |
105 | 3. *(optional)* Set up the Let's Encrypt integration.
106 |
107 | **Note**: If you are using Cloudflare, you might consider using their [free SSL/TLS encryption](https://www.cloudflare.com/ssl/)
108 | setup as a simpler alternative. But if you would prefer to have full control, read on.
109 |
110 | Make sure the environment variables required for the Let's Encrypt integration are set.
111 |
112 | You could set the environment variables in the `.env` file at the top level of the distribution project (not to be
113 | confused with `api/.env` which is used by the Symfony application). For example:
114 |
115 | ```
116 | LETSENCRYPT_USER_MAIL=user@example.com
117 | LEXICON_CLOUDFLARE_AUTH_TOKEN=9e06358f74cbce70602c22fc3279f0aee3077
118 | LEXICON_CLOUDFLARE_AUTH_USERNAME=user@example.com
119 | ```
120 |
121 | **Note**: If you are not using [Cloudflare DNS](https://www.cloudflare.com/dns/), please see [the documentation on
122 | how to pass the correct environment variables to Lexicon](https://github.com/adferrand/docker-letsencrypt-dns#configuring-dns-provider-and-authentication-to-dns-api).
123 |
124 | Configure the (sub)domains for which you want certificate(s) to be issued for in `docker-compose-prod/docker/letsencrypt/domains.conf`.
125 | For example, to request a wildcard certificate for `*.example.com` and `example.com`:
126 |
127 | ```
128 | *.example.com example.com autorestart-containers=api-platform_nginx-proxy_1
129 | ```
130 |
131 | **Note**: Replace the `api-platform` prefix in `api-platform_nginx-proxy_1` with your [Docker Compose project name
132 | (it defaults to the project directory name)](https://docs.docker.com/compose/reference/envvars/#compose_project_name).
133 |
134 | 4. Pull the Docker images.
135 |
136 | If you are **not** using the (optional) Let's Encrypt integration:
137 |
138 | $ docker-compose -f docker-compose-prod/docker-compose.yml pull
139 |
140 | If you are using the (optional) Let's Encrypt integration:
141 |
142 | $ docker-compose -f docker-compose-prod/docker-compose.yml -f docker-compose-prod/docker-compose.letsencrypt.yml pull
143 |
144 | 5. Bring up the services.
145 |
146 | If you are **not** using the (optional) Let's Encrypt integration:
147 |
148 | $ docker-compose -f docker-compose-prod/docker-compose.yml up -d
149 |
150 | If you are using the (optional) Let's Encrypt integration:
151 |
152 | $ docker-compose -f docker-compose-prod/docker-compose.yml -f docker-compose-prod/docker-compose.letsencrypt.yml up -d
153 |
--------------------------------------------------------------------------------