Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .github/workflows/docs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: docs
on:
pull_request: ~
push:
branches:
- master

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}

jobs:
doctor-rst:
runs-on: ubuntu-latest
name: Lint (DOCtor-RST)
steps:
- name: checkout
uses: actions/checkout@v7

- name: doctor-rst
uses: docker://oskarstark/doctor-rst:1.85.0
with:
args: docs --short --error-format=github
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
/cache
.phpunit.result.cache
.phpstan-cache
.doctor-rst.cache
8 changes: 8 additions & 0 deletions .symfony.bundle.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Configuration used by symfony.com to publish the docs of this bundle.
# See https://github.com/liip/LiipImagineBundle/blob/2.x/.symfony.bundle.yaml for the documented format.
branches: ["master"]
maintained_branches: ["master"]
doc_dir: "docs/"
current_branch: "master"
dev_branch: "master"
dev_branch_alias: "7.x"
134 changes: 39 additions & 95 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,117 +4,61 @@

# GraphQLite bundle

Symfony bundle for the `thecodingmachine/graphqlite` package.
It discovers your annotated controllers and types, builds the schema, exposes the `/graphql` endpoint through a PSR-7
bridge (with optional upload handling), and keeps the Symfony request available as the GraphQL context.
Symfony bundle for [GraphQLite](https://graphqlite.thecodingmachine.io): build a GraphQL API by adding PHP attributes
(`#[Query]`, `#[Mutation]`, `#[Type]`, `#[Field]`...) to your classes. The bundle exposes the `/graphql` endpoint and
integrates GraphQLite with Symfony services, Security, Validator, file uploads and cache.

Part of the bundle docs: https://graphqlite.thecodingmachine.io/docs/symfony-bundle

See [thecodingmachine/graphqlite](https://github.com/thecodingmachine/graphqlite).
**Documentation: [docs/index.rst](docs/index.rst)**

## Requirements

- PHP 8.1+
- Supports:
- Symfony 6.4/7.0/8.0
- GraphQLite ^8

## Installation

```bash
composer require thecodingmachine/graphqlite-bundle
```

Ensure the bundle is enabled (Symfony Flex does this automatically via `config/bundles.php` after `composer require`).

### Configure routes

Import the bundle routes to expose `/graphql`:

```yaml
# config/routes/graphqlite.yaml
graphqlite_bundle:
resource: '@GraphQLiteBundle/Resources/config/routes.php'
```

### Configure namespaces

Tell GraphQLite where to look for controllers and types:

```yaml
# config/packages/graphqlite.yaml
graphqlite:
namespace:
controllers: App\\GraphQL\\Controller
types:
- App\\GraphQL\\Type
- App\\Entity
```

## Quickstart

Create a controller with GraphQLite attributes:

```php
<?php
// src/GraphQL/Controller/HelloController.php
namespace App\GraphQL\Controller;

use TheCodingMachine\GraphQLite\Annotations\Query;
- Symfony 6.4, 7.x or 8.x
- GraphQLite ^8

final class HelloController
{
#[Query]
public function hello(string $name = 'world'): string
{
return sprintf('Hello %s', $name);
}
}
```
## Quick start

## Features
1. Install the bundle (Symfony Flex enables it and creates its configuration):

- Auto-discovers controllers and types from configured namespaces and registers GraphQLite services, query providers,
type mappers, and middleware through Symfony autoconfiguration
- Ships a `/graphql` route that converts Symfony requests to PSR-7 and keeps the Symfony request in the GraphQL context
- Passes the Symfony request as context to allow using them in queries/mutations
- Supports multipart uploads when `graphql-upload` is installed
- Integrates with Symfony Security for `#[Logged]`/`#[Right]` checks
- Expose `login`/`logout` mutations plus a `me` query (opt-out)
- Symfony Validator-based user input validation
- Lets you cap introspection, query depth, and query complexity via configuration
- Uses Symfony cache (APCu or PHP files) for schema caching
- Includes a `graphqlite:dump-schema` console command to export GraphQL SDL
```bash
composer require thecodingmachine/graphqlite-bundle
```

## Schema caching
2. Tell GraphQLite where your GraphQL classes live:

Building the schema means reading every controller and type, so the result is cached. The
`schema.auto_reload` option decides whether that cache is checked against your files on each request.
It defaults to `'%kernel.debug%'`, so you see your changes while you work and deployed environments do
not pay for the check.
```yaml
# config/packages/graphqlite.yaml
graphqlite:
namespace:
controllers: App\GraphQL\Controller\
types:
- App\Entity\
- App\GraphQL\Input\
```

Set it yourself when an environment does not fit that rule, such as a staging server that runs with
debug on but should still serve a cached schema:
3. Write your first query and call `/graphql`:

```yaml
# config/packages/graphqlite.yaml
graphqlite:
schema:
auto_reload: false
```
```php
// src/GraphQL/Controller/HelloController.php
namespace App\GraphQL\Controller;

With `auto_reload: true` an edited controller or type shows up on the next request. With
`auto_reload: false` the cached schema is trusted until the cache is cleared, so a deploy that changes
a controller, a type or one of their attributes has to clear it. `cache:clear` does that. Note that
with the APCu adapter the console and the web server each keep their own copy, so the PHP processes
serving requests need restarting as well.
use TheCodingMachine\GraphQLite\Annotations\Query;

## GraphiQL (playground)
class HelloController
{
#[Query]
public function hello(string $name = 'world'): string
{
return sprintf('Hello %s', $name);
}
}
```

The bundle wires Overblog’s GraphiQL bundle if it is installed. See https://github.com/overblog/GraphiQLBundle for
enabling the UI alongside the `/graphql` endpoint.
Continue with the [getting started tutorial](docs/getting-started.rst).

## Development

- Tests: `vendor/bin/phpunit`
- Tests: `vendor/bin/simple-phpunit`
- Static analysis: `composer phpstan`
- The code examples of `docs/` are tested: they must stay identical to the classes of `tests/DocsFixtures/`
(see `tests/DocsExamplesTest.php`).
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "6.0.x-dev"
"dev-master": "7.x-dev"
}
},
"prefer-stable": true
Expand Down
2 changes: 1 addition & 1 deletion config-template/packages/graphqlite.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Read more about GraphQLite available options at: https://graphqlite.thecodingmachine.io/docs/symfony-bundle
# Read more about GraphQLite bundle options at: https://github.com/thecodingmachine/graphqlite-bundle/blob/master/docs/configuration.rst
graphqlite:
namespace:
controllers: App\GraphqlController\
Expand Down
35 changes: 35 additions & 0 deletions docs/.doctor-rst.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
rules:
american_english: ~
avoid_repetetive_words: ~
blank_line_after_anchor: ~
blank_line_after_directive: ~
blank_line_before_directive: ~
correct_code_block_directive_based_on_the_content: ~
ensure_bash_prompt_before_composer_command: ~
ensure_exactly_one_space_before_directive_type: ~
ensure_exactly_one_space_between_link_definition_and_link: ~
ensure_link_definition_contains_valid_url: ~
indention: ~
lowercase_as_in_use_statements: ~
max_blank_lines:
max: 2
no_app_console: ~
no_blank_line_after_filepath_in_php_code_block: ~
no_blank_line_after_filepath_in_yaml_code_block: ~
no_composer_req: ~
no_merge_conflict: ~
no_namespace_after_use_statements: ~
no_php_open_tag_in_code_block_php_directive: ~
only_backslashes_in_namespace_in_php_code_block: ~
only_backslashes_in_use_statements_in_php_code_block: ~
ordered_use_statements: ~
php_prefix_before_bin_console: ~
replacement: ~
short_array_syntax: ~
space_between_label_and_link_in_doc: ~
space_between_label_and_link_in_ref: ~
title_underline_length_must_match_title_length: ~
typo: ~
unused_links: ~
valid_use_statements: ~
yaml_instead_of_yml_suffix: ~
56 changes: 56 additions & 0 deletions docs/cache-and-performance.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
Cache and Performance
=====================

Reading the attributes of every class to build the schema is slow, so
GraphQLite caches the result. The bundle configures this cache for you.

How the Schema Is Cached
------------------------

The cache is stored in APCu when the extension is available (and enabled for
the current SAPI), and in PHP files in the Symfony cache directory
(``var/cache/<env>/``) otherwise.

The ``schema.auto_reload`` option (see :doc:`configuration`) decides when
the cache is refreshed:

* ``true`` (default in ``dev``): GraphQLite checks your controller and type
files on every request and refreshes the schema when one of them changed.
Edit a class, reload, done;
* ``false`` (default in ``prod``): the schema is built once and kept until
the cache is cleared. This is the fastest mode.

Deploying to Production
-----------------------

Run the usual cache commands as part of your deployment:

.. code-block:: terminal

$ php bin/console cache:clear --env=prod

The bundle registers its cache as a Symfony *cache clearer*, so
``cache:clear`` also clears the GraphQLite cache.

.. caution::

APCu memory is separate for each PHP process pool: the console (CLI) cannot
clear the APCu cache of your web server (PHP-FPM, FrankenPHP...). When the
schema is cached in APCu, restart or reload PHP after deploying, as you
probably already do for OPcache.

Performance Tips
----------------

* Keep the ``namespace`` options narrow: list only the namespaces that
contain GraphQL classes, rather than ``App\``. Fewer classes to scan means a
faster cache warm-up and a faster ``dev`` environment;
* avoid the "N+1 queries" problem when fetching related objects: use the
`prefetch method`_ of GraphQLite to load them in batch;
* fetch only what the client asked for with the `query plan`_;
* limit the cost of the queries clients can send with
``maximum_query_complexity`` and ``maximum_query_depth`` (see
:doc:`security`).

.. _`prefetch method`: https://graphqlite.thecodingmachine.io/docs/prefetch-method
.. _`query plan`: https://graphqlite.thecodingmachine.io/docs/query-plan
29 changes: 29 additions & 0 deletions docs/commands.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
Console Commands
================

Dumping the Schema
------------------

The ``graphqlite:dump-schema`` command prints the GraphQL schema built from
your code, in the GraphQL Schema Definition Language (SDL):

.. code-block:: terminal

$ php bin/console graphqlite:dump-schema

Use the ``--output`` (or ``-o``) option to write it to a file:

.. code-block:: terminal

$ php bin/console graphqlite:dump-schema --output=schema.graphql

Types and fields are sorted by name, so the output is stable. A few use cases:

* check that a class is picked up by GraphQLite after changing the
``namespace`` configuration;
* commit the schema file and review its changes in pull requests, to spot
breaking changes of your API;
* generate typed code for your frontend (TypeScript types, query hooks...)
with tools such as `GraphQL Code Generator`_.

.. _`GraphQL Code Generator`: https://the-guild.dev/graphql/codegen
Loading
Loading