The formatting of errors that implement the ClientAware
interface no longer
contains the key category
. This includes both built-in and user-defined errors.
throw new \GraphQL\Error\Error('msg');
Formatting before the change:
'errors' => [
[
'message' => 'msg',
'extensions' => [
'category' => 'graphql',
],
],
]
After the change:
'errors' => [
[
'message' => 'msg',
],
]
The method ClientAware::getCategory()
was removed, you may also remove it from your implementations:
use GraphQL\Error\ClientAware;
class MyException extends \Exception implements ClientAware
{
public function isClientSafe(): bool
{
return true;
}
- public function getCategory(): string
- {
- return 'my-category';
- }
}
You can always switch to custom error formatting to revert to the old format.
The argument bool $exitWhenDone
was removed from StandardServer::send500Error()
and StandardServer::handleRequest()
.
Exit yourself if you need to:
$server = new GraphQL\Server\StandardServer();
-$server->handleRequest($body, true);
+$server->handleRequest($body);
+exit;
Impact: Major
This change may break API clients if they were sending loose variable values.
See Examples
Consider the following query:
query ($intQueryVariable: Int) {
test(intInput: $intQueryVariable)
}
What happens if we pass non-integer values as $intQueryVariable
:
[true, false, 1, 0, 0.0, 'true', 'false', '1', '0', '0.0', [], [0,1]]
bool(true):
0.13.x: coerced to int(1)
14.x.x: Error: Variable "$queryVariable" got invalid value true; Expected type Int; Int cannot represent non-integer value: true
bool(false):
0.13.x: coerced to int(0)
14.x.x: Error: Variable "$queryVariable" got invalid value false; Expected type Int; Int cannot represent non-integer value: false
string(1) "1"
0.13.x: was coerced to int(1)
14.x.x: Error: Variable "$queryVariable" got invalid value "1"; Expected type Int; Int cannot represent non-integer value: 1
string(1) "0"
0.13.x: was coerced to int(0)
14.x.x: Error: Variable "$queryVariable" got invalid value "0"; Expected type Int; Int cannot represent non-integer value: 0
string(3) "0.0"
0.13.x: was coerced to int(0)
14.x.x: Error: Variable "$queryVariable" got invalid value "0.0"; Expected type Int; Int cannot represent non-integer value: 0.0
Did not change:
int(1): coerced to int(1)
int(0) was coerced to int(0)
float(0) was coerced to int(0)
string(4) "true":
Error: Variable "$queryVariable" got invalid value "true"; Expected type Int; Int cannot represent non 32-bit signed integer value: true
string(5) "false":
Error: Variable "$queryVariable" got invalid value "false"; Expected type Int; Int cannot represent non 32-bit signed integer value: false
array(0) {}
Error: Variable "$queryVariable" got invalid value []; Expected type Int; Int cannot represent non 32-bit signed integer value: []
array(2) { [0]=> int(0) [1]=> int(1) }
Error: Variable "$queryVariable" got invalid value [0,1]; Expected type Int; Int cannot represent non 32-bit signed integer value: [0,1]
query ($queryVariable: Float) {
test(floatInput: $queryVariable)
}
bool(true)
0.13.x: was coerced to float(1)
14.x.x: Error: Variable "$queryVariable" got invalid value true; Expected type Float; Float cannot represent non numeric value: true
bool(false)
0.13.x: was coerced to float(0)
14.x.x: Error: Variable "$queryVariable" got invalid value false; Expected type Float; Float cannot represent non numeric value: false
string(1) "1"
0.13.x: was coerced to float(1)
14.x.x: Error: Variable "$queryVariable" got invalid value "1"; Expected type Float; Float cannot represent non numeric value: 1
string(1) "0"
0.13.x: was coerced to float(0)
14.x.x: Error: Variable "$queryVariable" got invalid value "0"; Expected type Float; Float cannot represent non numeric value: 0
string(3) "0.0"
0.13.x: was coerced to float(0)
14.x.x: Error: Variable "$queryVariable" got invalid value "0.0"; Expected type Float; Float cannot represent non numeric value: 0.0
query ($queryVariable: String) {
test(stringInput: $queryVariable)
}
bool(true)
0.13.x: was coerced to string(1) "1"
14.x.x: Error: Variable "$queryVariable" got invalid value true; Expected type String; String cannot represent a non string value: true
bool(false)
0.13.x: was coerced to string(0) ""
14.x.x: Error: Variable "$queryVariable" got invalid value false; Expected type String; String cannot represent a non string value: false
int(1)
0.13.x: was coerced to string(1) "1"
14.x.x: Error: Variable "$queryVariable" got invalid value 1; Expected type String; String cannot represent a non string value: 1
int(0)
0.13.x: was coerced to string(1) "0"
14.x.x: Error: Variable "$queryVariable" got invalid value 0; Expected type String; String cannot represent a non string value: 0
float(0)
0.13.x: was coerced to string(1) "0"
14.x.x: Error: Variable "$queryVariable" got invalid value 0; Expected type String; String cannot represent a non string value: 0
Impact: Medium
- Dropped previously deprecated
GraphQL\Schema
. UseGraphQL\Type\Schema
. - Renamed
GraphQL\Error\Debug
toGraphQL\Error\DebugFlag
. - Debug flags in
GraphQL\Executor\ExecutionResult
,GraphQL\Error\FormattedError
andGraphQL\Server\ServerConfig
do not acceptboolean
value anymore butint
only (pass values ofGraphQL\Error\DebugFlag
constants) $positions
inGraphQL\Error\Error
are not nullable anymore. Same can be expressesed by passing empty array.
Impact: Minor
Could affect developer tools relying on old introspection format. Replaced with Directive Locations.
Impact: Minor
- Removal of
VariablesDefaultValueAllowed
validation rule. All variables may now specify a default value. - Renamed
ProvidedNonNullArguments
toProvidedRequiredArguments
(no longer require values to be provided to non-null arguments which provide a default value).
Could affect projects using custom sets of validation rules.
Impact: Minor
Can only affect a few projects that were somehow customizing deferreds or the default sync promise adapter.
Impact: Minor
Can affect projects relying on BreakingChangesFinder
utility in their CI.
Following types of changes were renamed:
- `NON_NULL_ARG_ADDED` to `REQUIRED_ARG_ADDED`
- `NON_NULL_INPUT_FIELD_ADDED` to `REQUIRED_INPUT_FIELD_ADDED`
- `NON_NULL_DIRECTIVE_ARG_ADDED` to `REQUIRED_DIRECTIVE_ARG_ADDED`
- `NULLABLE_INPUT_FIELD_ADDED` to `OPTIONAL_INPUT_FIELD_ADDED`
- `NULLABLE_ARG_ADDED` to `OPTIONAL_ARG_ADDED`
Impact: Minor
Use GraphQL\Error\Error->getMessage()
instead.
Impact: Minor
The constants in \GraphQL\Type\TypeKind
were partly renamed and their values
have been changed to match their name instead of a numeric index.
Impact: Minor
Can affect projects relying on error messages parsing.
One example: added quotes around parentType.fieldName
in error message:
- Cannot return null for non-nullable field parentType.fieldName.
+ Cannot return null for non-nullable field "parentType.fieldName".
But expect other similar changes like this.
New minimum required version of PHP is 7.1+
Category and extensions assigned to errors are shown under extensions
key
$e = new Error(
'msg',
null,
null,
null,
null,
null,
['foo' => 'bar']
);
Formatting before the change:
'errors' => [
[
'message' => 'msg',
'category' => 'graphql',
'foo' => 'bar'
]
]
After the change:
'errors' => [
[
'message' => 'msg',
'extensions' => [
'category' => 'graphql',
'foo' => 'bar',
],
]
]
Note: if error extensions contain category
key - it has a priority over default category.
You can always switch to custom error formatting to revert to the old format.
It is disabled by default. To enable it, do the following
use GraphQL\Executor\Executor;
use GraphQL\Experimental\Executor\CoroutineExecutor;
Executor::setImplementationFactory([CoroutineExecutor::class, 'create']);
Please post your feedback about new executor at #397 Especially if you had issues (because it may become the default in one of the next releases)
Before the change:
type Foo implements Bar, Baz {
field: Type
}
After the change:
type Foo implements Bar & Baz {
field: Type
}
To allow for an adaptive migration, use allowLegacySDLImplementsInterfaces
option of parser:
Parser::parse($source, [ 'allowLegacySDLImplementsInterfaces' => true])
AbstractValidationRule
renamed toValidationRule
(NSGraphQL\Validator\Rules
)AbstractQuerySecurity
renamed toQuerySecurityRule
(NSGraphQL\Validator\Rules
)FindBreakingChanges
renamed toBreakingChangesFinder
(NSGraphQL\Utils
)
GraphQL\Type\Definition\ResolveInfo
now takes 10 arguments instead of one array.
Dropped support for PHP 5.5. This release still supports PHP 5.6 and PHP 7.0 But the next major release will require PHP7.1+
As null might be a valid value custom types need to throw an
Exception inside parseLiteral()
, parseValue()
and serialize()
.
Returning null from any of these methods will now be treated as valid result.
A new parameter was added to parseLiteral()
, which also needs to be added to any custom scalar type extending from ScalarType
Before:
class MyType extends ScalarType {
...
public function parseLiteral($valueNode) {
//custom implementation
}
}
After:
class MyType extends ScalarType {
...
public function parseLiteral($valueNode, array $variables = null) {
//custom implementation
}
}
Descriptions now need to be inside Strings or BlockStrings in order to be picked up as
description. If you want to keep the old behaviour you can supply the option commentDescriptions
to BuildSchema::buildAST(), BuildSchema::build() or Printer::doPrint().
Here is the official way now to define descriptions in the graphQL language:
Old:
# Description
type Dog {
...
}
New:
"Description"
type Dog {
...
}
"""
Long Description
"""
type Dog {
...
}
That's because description in AST is now a separate node, not just a string. Make sure to renew caches.
See deprecation notices for previous versions in details.
Before the change:
{
"queryId": "persisted-query-id",
"operation": "QueryFromPersistedDocument",
"variables": {}
}
After the change:
{
"queryId": "persisted-query-id",
"operationName": "QueryFromPersistedDocument",
"variables": {}
}
This naming is aligned with graphql-express version.
Most users won't be affected. It may affect you only if you do your own manipulations with exported AST.
Example of json-serialized AST before the change:
{
"kind": "Field",
"loc": null,
"name": {
"kind": "Name",
"loc": null,
"value": "id"
},
"alias": null,
"arguments": [],
"directives": [],
"selectionSet": null
}
After the change:
{
"kind": "Field",
"name": {
"kind": "Name",
"value": "id"
},
"arguments": [],
"directives": []
}
It allows us to leverage ::class
constant, generators
and other features of newer PHP versions.
By default exceptions thrown in resolvers will be reported with generic message "Internal server error"
.
Only exceptions implementing interface GraphQL\Error\ClientAware
and claiming themselves as safe
will
be reported with full error message.
This breaking change is done to avoid information leak in production when unhandled exceptions were reported to clients (e.g. database connection errors, file access errors, etc).
Also every error reported to client now has new category
key which is either graphql
or internal
.
Exceptions implementing ClientAware
interface may define their own custom categories.
During development or debugging use $executionResult->toArray(true)
. It will add debugMessage
key to
each error entry in result. If you also want to add trace
for each error - pass flags instead:
use GraphQL\Error\FormattedError;
$debug = FormattedError::INCLUDE_DEBUG_MESSAGE | FormattedError::INCLUDE_TRACE;
$result = GraphQL::executeAndReturnResult(/*args*/)->toArray($debug);
To change default "Internal server error"
message to something else, use:
GraphQL\Error\FormattedError::setInternalErrorMessage("Unexpected error");
This change only affects default error reporting mechanism. If you set your own error formatter using
$executionResult->setErrorFormatter($myFormatter)
you won't be affected by this change.
If you need to revert to old behavior temporary, use:
GraphQL::executeAndReturnResult(/**/)
->setErrorFormatter('\GraphQL\Error\Error::formatError')
->toArray();
But note that this is deprecated format and will be removed in future versions.
In general, if new default formatting doesn't work for you - just set your own error formatter.
Previously any callable was accepted by DocumentValidator as validation rule. Now only instances of
GraphQL\Validator\Rules\AbstractValidationRule
are allowed.
If you were using custom validation rules, just wrap them with
GraphQL\Validator\Rules\CustomValidationRule
(created for backwards compatibility).
Before:
use GraphQL\Validator\DocumentValidator;
$myRule = function(ValidationContext $context) {};
DocumentValidator::validate($schema, $ast, [$myRule]);
After:
use GraphQL\Validator\Rules\CustomValidationRule;
use GraphQL\Validator\DocumentValidator;
$myRule = new CustomValidationRule('MyRule', function(ValidationContext $context) {});
DocumentValidator::validate($schema, $ast, [$myRule]);
Also DocumentValidator::addRule()
signature changed.
Before the change:
use GraphQL\Validator\DocumentValidator;
$myRule = function(ValidationContext $context) {};
DocumentValidator::addRule('MyRuleName', $myRule);
After the change:
use GraphQL\Validator\DocumentValidator;
$myRule = new CustomValidationRulefunction('MyRule', ValidationContext $context) {});
DocumentValidator::addRule($myRule);
It helps us unserialize AST from array lazily. This change affects you only if you use array_
functions with AST or mutate AST directly.
Before the change:
new GraphQL\Language\AST\DocumentNode([
'definitions' => array(/*...*/)
]);
After the change:
new GraphQL\Language\AST\DocumentNode([
'definitions' => new NodeList([/*...*/])
]);
On invalid client input (parseValue
and parseLiteral
) they throw standard GraphQL\Error\Error
but when they encounter invalid output (in serialize
) they throw GraphQL\Error\InvariantViolation
.
Previously they were throwing GraphQL\Error\UserError
. This exception is no longer used so make sure
to adjust if you were checking for this error in your custom error formatters.
See #35
Method is renamed to GraphQL\GraphQL::executeQuery
. Old method name is still available,
but will trigger deprecation warning in the next version.
Use GraphQL\GraphQL::executeQuery()->toArray()
instead.
Old method still exists, but will trigger deprecation warning in next version.
Old class still exists, but will trigger deprecation warning in next version.
Old class still exists, but triggers deprecation warning when referenced.
If you were using config validation in previous versions, replace:
GraphQL\Type\Definition\Config::enableValidation();
with:
$schema->assertValid();
See #148
Use new PSR-7 compliant implementation instead.
Use schema typeLoader option instead.
When using the library on async platforms use separate method GraphQL::promiseToExecute()
.
It requires promise adapter in it's first argument and always returns a Promise
.
Old methods GraphQL::execute
and GraphQL::executeAndReturnResult
still work in backwards-compatible manner,
but they are deprecated and will be removed eventually.
Same applies to Executor: use Executor::promiseToExecute()
vs Executor::execute()
.
All of those changes apply to those who extends various parts of this library. If you only use the library and don't try to extend it - everything should work without breaks.
When passing custom directives to schema, default directives (like @skip
and @include
)
are not added to schema automatically anymore. If you need them - add them explicitly with
your other directives.
Before the change:
$schema = new Schema([
// ...
'directives' => [$myDirective]
]);
After the change:
$schema = new Schema([
// ...
'directives' => array_merge(GraphQL::getInternalDirectives(), [$myDirective])
]);
Most of the protected
properties and methods of GraphQL\Schema
were changed to private
.
Please use public interface instead.
Node kind constants were extracted from GraphQL\Language\AST\Node
to
separate class GraphQL\Language\AST\NodeKind
AST node classes were renamed to disambiguate with types. e.g.:
GraphQL\Language\AST\Field -> GraphQL\Language\AST\FieldNode
GraphQL\Language\AST\OjbectValue -> GraphQL\Language\AST\OjbectValueNode
etc.
Old names are still available via class_alias
defined in src/deprecated.php
.
This file is included automatically when using composer autoloading.
There are several deprecations which still work, but trigger E_USER_DEPRECATED
when used.
For example GraphQL\Executor\Executor::setDefaultResolveFn()
is renamed to setDefaultResolver()
but still works with old name.
There are a few new breaking changes in v0.7.0 that were added to the graphql-js reference implementation with the spec of April2016
You can now pass a custom context to the GraphQL::execute
function that is available in all resolvers as 3rd argument.
This can for example be used to pass the current user etc.
Make sure to update all calls to GraphQL::execute
, GraphQL::executeAndReturnResult
, Executor::execute
and all
'resolve'
callbacks in your app.
Before v0.7.0 GraphQL::execute
signature looked this way:
GraphQL::execute(
$schema,
$query,
$rootValue,
$variables,
$operationName
);
Starting from v0.7.0 the signature looks this way (note the new $context
argument):
GraphQL::execute(
$schema,
$query,
$rootValue,
$context,
$variables,
$operationName
);
Before v.0.7.0 resolve callbacks had following signature:
/**
* @param mixed $object The parent resolved object
* @param array $args Input arguments
* @param ResolveInfo $info ResolveInfo object
* @return mixed
*/
function resolveMyField($object, array $args, ResolveInfo $info) {
//...
}
Starting from v0.7.0 the signature has changed to (note the new $context
argument):
/**
* @param mixed $object The parent resolved object
* @param array $args Input arguments
* @param mixed $context The context object that was passed to GraphQL::execute
* @param ResolveInfo $info ResolveInfo object
* @return mixed
*/
function resolveMyField($object, array $args, $context, ResolveInfo $info){
//...
}
The signature of the Schema constructor now accepts an associative config array instead of positional arguments:
Before v0.7.0:
$schema = new Schema($queryType, $mutationType);
Starting from v0.7.0:
$schema = new Schema([
'query' => $queryType,
'mutation' => $mutationType,
'types' => $arrayOfTypesWithInterfaces // See 3.
]);
There are edge cases when GraphQL cannot infer some types from your schema. One example is when you define a field of interface type and object types implementing this interface are not referenced anywhere else.
In such case object types might not be available when an interface is queried and query validation will fail. In that case, you need to pass the types that implement the interfaces directly to the schema, so that GraphQL knows of their existence during query validation.
For example:
$schema = new Schema([
'query' => $queryType,
'mutation' => $mutationType,
'types' => $arrayOfTypesWithInterfaces
]);
Note that you don't need to pass all types here - only those types that GraphQL "doesn't see" automatically. Before v7.0.0 the workaround for this was to create a dumb (non-used) field per each "invisible" object type.
Also see webonyx/graphql-php#38