diff --git a/docs/master/api-reference/resolvers.md b/docs/master/api-reference/resolvers.md
index e18ab249c7..963695fb3a 100644
--- a/docs/master/api-reference/resolvers.md
+++ b/docs/master/api-reference/resolvers.md
@@ -10,12 +10,7 @@ Resolvers are always called with the same 4 arguments:
 use GraphQL\Type\Definition\ResolveInfo;
 use Nuwave\Lighthouse\Support\Contracts\GraphQLContext;
 
-public function resolve(
-    $rootValue,
-    array $args,
-    GraphQLContext $context,
-    ResolveInfo $resolveInfo
-)
+function ($rootValue, array $args, GraphQLContext $context, ResolveInfo $resolveInfo)
 ```
 
 1. `$rootValue`: The result that was returned from the parent field.
@@ -27,15 +22,15 @@ Lighthouse passes in an instance of `Nuwave\Lighthouse\Schema\Context` by defaul
 4. `ResolveInfo $resolveInfo`: Information about the query itself,
 such as the execution state, the field name, path to the field from the root, and more.
 
+The return value of this must fit the return type defined for the corresponding field from the schema. 
+
 ## Complexity function signature
 
 The complexity function is used to calculate a query complexity score for a field.
 You can define your own complexity function with the [@complexity](../api-reference/directives.md#complexity) directive.
 
 ```php
-<?php
-
-public function complexity(int $childrenComplexity, array $args): int
+function (int $childrenComplexity, array $args): int
 ```
 
 1. `$childrenComplexity`: The complexity of the children of the field. In case you expect to return
diff --git a/docs/master/getting-started/installation.md b/docs/master/getting-started/installation.md
index 4e70e78ae7..14b64da4b7 100644
--- a/docs/master/getting-started/installation.md
+++ b/docs/master/getting-started/installation.md
@@ -1,5 +1,8 @@
 # Installation
 
+The following section teaches you how to install Lighthouse in your project.
+Make sure you familiarize yourself with [the basics](../the-basics/schema.md) before diving in.
+
 ## Install via composer
 
 ```bash
diff --git a/docs/master/guides/plugin-development.md b/docs/master/guides/plugin-development.md
index 85d184f68e..f8def94ff8 100644
--- a/docs/master/guides/plugin-development.md
+++ b/docs/master/guides/plugin-development.md
@@ -31,3 +31,22 @@ You can add your custom directives to Lighthouse by listening for the [`Register
 
 Check out [the test suite](https://github.com/nuwave/lighthouse/tree/master/tests/Integration/Events/RegisterDirectiveNamespacesTest.php)
 for an example of how this works.
+
+## Change the default resolver
+
+The first priority when looking for a resolver is always given to `FieldResolver` directives.
+
+After that, Lighthouse attempts to find a default resolver.
+
+The interface [`\Nuwave\Lighthouse\Support\Contracts\ProvidesResolver`](../../../src/Support/Contracts/ProvidesResolver.php)
+is expected to provide a resolver in case no resolver directive is defined for a field.
+
+If the field is defined on the root `Query` or `Mutation` types,
+Lighthouse's default implementation looks for a class with the capitalized name
+of the field in the configured default location.
+
+Non-root fields fall back to [webonyx's default resolver](http://webonyx.github.io/graphql-php/data-fetching/#default-field-resolver).
+You may overwrite this by passing a `callable` to `\GraphQL\Executor\Executor::setDefaultFieldResolver`. 
+
+When the field is defined on the root `Subscription` type, the [`\Nuwave\Lighthouse\Support\Contracts\ProvidesSubscriptionResolver`](../../../src/Support/Contracts/ProvidesSubscriptionResolver.php)
+interface is used instead.
diff --git a/docs/master/the-basics/fields.md b/docs/master/the-basics/fields.md
index 92f4e22615..3538331614 100644
--- a/docs/master/the-basics/fields.md
+++ b/docs/master/the-basics/fields.md
@@ -1,12 +1,19 @@
 # Fields
 
-To fetch data from your GraphQL endpoint, you need to define resolvers for your fields.
-Lighthouse makes this easy by providing easy to use, pre-built resolvers that work
-great together with your Eloquent models.
+The entrypoints to any GraphQL API are the fields of the root types `Query`, `Mutation` and `Subscription`.
 
-## Hello World!
+*Every* field has a function associated with it that is called when the field
+is requested as part of a query. This function is called a **resolver**.
+
+The following section will teach you how to define a resolver for your fields
+and how you can utilize Lighthouse's built-in resolvers.
+
+## Resolving fields
 
 As is the tradition of our people, this section will teach you how to say "hello world!" through Lighthouse.
+
+### Schema definition
+
 The following schema defines a simple field called `hello` that returns a `String`.
 
 ```graphql
@@ -15,8 +22,14 @@ type Query {
 }
 ```
 
-You need to implement the actual resolver next. Lighthouse looks for a class with the capitalized name of the
-field in `App\GraphQL\Queries` and calls its `resolve` function.
+You need to implement the actual resolver next.
+
+### Defining resolvers
+
+By default, Lighthouse looks for a class with the capitalized name of the field in `App\GraphQL\Queries`
+or `App\GraphQL\Mutations` and calls its `resolve` function with [the usual resolver arguments](../api-reference/resolvers.md#resolver-function-signature).
+
+In this case, our field is called `hello` so we need to define our class as follows:
 
 ```php
 <?php
@@ -32,6 +45,14 @@ class Hello
 }
 ```
 
+The easiest way to create such a class is to use the built in `artisan` commands
+`lighthouse:query` and `lighthouse:mutation`. They both take a single argument:
+the name of the field you want to generate.
+
+For example, this is how you generate a class for the field `hello`:
+
+    php artisan lighthouse:query Hello
+
 Now your schema can be queried.
 
 ```graphql
@@ -50,6 +71,165 @@ And will return the following response:
 }
 ```
 
+### Fields with arguments
+
+As we learned, *every* field has a resolver function associated with it.
+Just like functions, fields can take arguments to control their behaviour.
+
+Let's construct a query that greets the user. We add a required argument `name`
+that is used to construct the greeting.
+
+```graphql
+type Query {
+    greet(name: String!): String
+}
+```
+
+A minimal implementation of the field could look something like this.
+The skeleton for this class can be created using `php artisan lighthouse:query Greet`.
+
+The second argument of the resolver function is an associative array of the
+arguments that are passed to the query. 
+
+```php
+<?php
+
+namespace App\GraphQL\Queries;
+
+class Greet
+{
+    public function resolve($rootValue, array $args): string
+    {
+        return "Hello, {$args['name']}!";
+    }
+}
+```
+
+We can call this query, passing a `name` of our choosing.
+
+```graphql
+{
+  greet(name: "Foo")
+}
+```
+
+And receive a friendly greeting.
+
+```json
+{
+  "data": {
+    "greet": "Hello, Foo!"
+  }
+}
+```
+
+If we don't want to require the user to pass an argument, we can modify our schema
+and make the `name` optional and provide a default value.
+
+```graphql
+type Query {
+    greet(name: String = "you"): String
+}
+```
+
+Now we can use our query like this:
+
+```graphql
+{
+  greet
+}
+```
+
+```json
+{
+  "data": {
+    "greet": "Hello, you!"
+  }
+}
+```
+
+### Resolving non-root fields
+
+As mentioned, every field in the schema has a resolver - but what
+about fields that are not on one of the root types?
+
+```graphql
+type Query {
+  user: User!
+}
+
+type User {
+  id: ID!
+  name: String!
+  email: String
+}
+```
+
+Let's play through what happens when the client send's the following query:
+
+```graphql
+{
+  user {
+    id
+    name
+  }
+}
+```
+
+First, the resolver for `user` will be called. Let's suppose it returns an instance
+of `App\Model\User`.
+
+Next, the field sub-selection will be resolved - the two requested fields are `id` and `name`.
+Since we resolved the User already in the parent field, we do not want to fetch it again
+to get it's attributes.
+
+Conveniently, the first argument of each resolver is the return value of the parent
+field, in this case a User model.
+
+A naive implementation of a resolver for `id` might look like this:
+
+```php
+<?php
+
+use App\Models\User;
+
+function resolveUserId(User $user): string
+{
+    return $user->id;
+}
+```
+
+Writing out each such resolver would be pretty repetitive.
+We can utilize the fourth and final resolver argument `ResolveInfo`,
+which will give us access to the requested field name,
+to dynamically access the matching property.
+
+```php
+<?php
+
+use App\Models\User;
+use GraphQL\Type\Definition\ResolveInfo;
+use Nuwave\Lighthouse\Support\Contracts\GraphQLContext;
+
+function resolveUserAttribute(User $user, array $args, GraphQLContext $context, ResolveInfo $resolveInfo)
+{
+    return $user->{$resolveInfo->fieldName};
+}
+```
+
+Fortunately, the underlying GraphQL implementation already provides [a sensible default resolver](http://webonyx.github.io/graphql-php/data-fetching/#default-field-resolver),
+that plays quite nicely with the data you would typically return from
+a root resolver, e.g. `Eloquent` models or associative arrays.
+
+This means that in most cases, you will only have to provide resolvers for the
+root fields and make sure they return data in the proper shape.
+
+If you need to implement custom resolvers for fields that are not on one of the
+root types `Query` or `Mutation`, you can use either the
+[@field](../api-reference/directives.md#field) or [@method](../api-reference/directives.md#method) directive.
+
+You may also [change the default resolver](../guides/plugin-development.md#change-the-default-resolver) if you need.
+
 ## Query data
 
 Lighthouse provides many resolvers that are already built-in, so you do not have to define them yourself.
@@ -102,10 +282,10 @@ Will return the following result:
 }
 ```
 
-### Query with arguments
+### Adding query constraints
 
-You may have noticed how every field has to have a resolver function. In many ways, fields are similar to functions.
-Just like functions, fields can take arguments to make them more flexible.
+Lighthouse provides built-in directives to enhance your queries by giving
+additional query capabilities to the client.
 
 The following field allows you to fetch a single User by ID.
 
@@ -262,51 +442,3 @@ Lighthouse allows you to serve GraphQL subscriptions. Compared to queries and
 mutations, a more elaborate setup is required.
  
 [Read more about how to set up subscriptions](../extensions/subscriptions.md)
-
-## Custom resolvers
-
-Sometimes, the built-in directives just don't cut it - you need more control!
-Lighthouse allows you to implement your own resolver function for fields.
-
-By default, Lighthouse looks for a class with the capitalized name of the field in `App\GraphQL\Queries`
-or `App\GraphQL\Mutations` and calls its `resolve` function with [the usual resolver arguments](../api-reference/resolvers.md#resolver-function-signature).
-If you stick to that convention, you will not need to specify a directive at all.
-
-For example, the following field:
-
-```graphql
-type Query {
-  latestPost: Post!
-}
-```
-
-expects a class like this:
-
-```php
-<?php
-
-namespace App\GraphQL\Queries;
-
-use App\Post;
-use GraphQL\Type\Definition\ResolveInfo;
-use Nuwave\Lighthouse\Support\Contracts\GraphQLContext;
-
-class LatestPost
-{
-    public function resolve($rootValue, array $args, GraphQLContext $context, ResolveInfo $resolveInfo): Post
-    {
-        return Post::orderBy('published_at', 'DESC')->first();
-    }
-}
-```
-
-The easiest way to create such a class is to use the built in artisan commands
-`lighthouse:query` and `lighthouse:mutation`. They both take a single argument:
-the name of the field you want to generate.
-
-For example, this is how you generate a class for the field `latestPost`:
-
-    php artisan lighthouse:query LatestPost
-
-If you need to implement custom resolvers for fields that are not on one of the
-root types `Query` or `Mutation`, you can use the [@field](../api-reference/directives.md#field) directive. 
diff --git a/docs/master/the-basics/types.md b/docs/master/the-basics/types.md
index c19320af9f..904c6a2f5b 100644
--- a/docs/master/the-basics/types.md
+++ b/docs/master/the-basics/types.md
@@ -7,7 +7,7 @@ look into the [GraphQL documentation](https://graphql.org/learn/schema/)
 ## Object Type
 
 Object types define the resources of your API and are closely related to Eloquent models.
-They must have a unique name and have a set of fields.
+They must have a unique name and contain a set of fields.
 
 ```graphql
 type User {
diff --git a/src/Defer/DeferrableDirective.php b/src/Defer/DeferrableDirective.php
index 624848e5f2..f51e67dfc2 100644
--- a/src/Defer/DeferrableDirective.php
+++ b/src/Defer/DeferrableDirective.php
@@ -51,13 +51,13 @@ public function name(): string
      */
     public function handleField(FieldValue $value, Closure $next): FieldValue
     {
-        $resolver = $value->getResolver();
+        $previousResolver = $value->getResolver();
         $fieldType = $value->getField()->type;
 
         $value->setResolver(
-            function ($root, array $args, GraphQLContext $context, ResolveInfo $resolveInfo) use ($resolver, $fieldType) {
-                $wrappedResolver = function () use ($resolver, $root, $args, $context, $resolveInfo) {
-                    return $resolver($root, $args, $context, $resolveInfo);
+            function ($root, array $args, GraphQLContext $context, ResolveInfo $resolveInfo) use ($previousResolver, $fieldType) {
+                $wrappedResolver = function () use ($previousResolver, $root, $args, $context, $resolveInfo) {
+                    return $previousResolver($root, $args, $context, $resolveInfo);
                 };
                 $path = implode('.', $resolveInfo->path);
 
@@ -67,7 +67,7 @@ function ($root, array $args, GraphQLContext $context, ResolveInfo $resolveInfo)
 
                 return $this->defer->isStreaming()
                     ? $this->defer->findOrResolve($wrappedResolver, $path)
-                    : $resolver($root, $args, $context, $resolveInfo);
+                    : $previousResolver($root, $args, $context, $resolveInfo);
             }
         );
 
diff --git a/src/LighthouseServiceProvider.php b/src/LighthouseServiceProvider.php
index 3c1334db65..6dbd6f9217 100644
--- a/src/LighthouseServiceProvider.php
+++ b/src/LighthouseServiceProvider.php
@@ -2,6 +2,7 @@
 
 namespace Nuwave\Lighthouse;
 
+use Closure;
 use Illuminate\Support\Arr;
 use Illuminate\Support\Str;
 use Illuminate\Validation\Validator;
@@ -14,10 +15,12 @@
 use Nuwave\Lighthouse\Console\ScalarCommand;
 use Illuminate\Contracts\Container\Container;
 use Nuwave\Lighthouse\Console\MutationCommand;
+use Nuwave\Lighthouse\Schema\ResolverProvider;
 use Nuwave\Lighthouse\Console\InterfaceCommand;
 use Nuwave\Lighthouse\Execution\ContextFactory;
 use Nuwave\Lighthouse\Execution\GraphQLRequest;
 use Nuwave\Lighthouse\Execution\SingleResponse;
+use Nuwave\Lighthouse\Schema\Values\FieldValue;
 use Nuwave\Lighthouse\Console\ClearCacheCommand;
 use Nuwave\Lighthouse\Console\PrintSchemaCommand;
 use Nuwave\Lighthouse\Execution\GraphQLValidator;
@@ -32,8 +35,10 @@
 use Nuwave\Lighthouse\Schema\Factories\DirectiveFactory;
 use Nuwave\Lighthouse\Support\Contracts\CreatesResponse;
 use Nuwave\Lighthouse\Schema\Source\SchemaSourceProvider;
+use Nuwave\Lighthouse\Support\Contracts\ProvidesResolver;
 use Nuwave\Lighthouse\Support\Contracts\CanStreamResponse;
 use Nuwave\Lighthouse\Support\Http\Responses\ResponseStream;
+use Nuwave\Lighthouse\Support\Contracts\ProvidesSubscriptionResolver;
 
 class LighthouseServiceProvider extends ServiceProvider
 {
@@ -123,6 +128,18 @@ public function register(): void
             );
         });
 
+        $this->app->bind(ProvidesResolver::class, ResolverProvider::class);
+        $this->app->bind(ProvidesSubscriptionResolver::class, function (): ProvidesSubscriptionResolver {
+            return new class() implements ProvidesSubscriptionResolver {
+                public function provideSubscriptionResolver(FieldValue $fieldValue): Closure
+                {
+                    throw new \Exception(
+                       'Add the SubscriptionServiceProvider to your config/app.php to enable subscriptions.'
+                   );
+                }
+            };
+        });
+
         if ($this->app->runningInConsole()) {
             $this->commands([
                 ClearCacheCommand::class,
diff --git a/src/Schema/Directives/Fields/AllDirective.php b/src/Schema/Directives/Fields/AllDirective.php
index 6e8dac4169..a5b261cdb4 100644
--- a/src/Schema/Directives/Fields/AllDirective.php
+++ b/src/Schema/Directives/Fields/AllDirective.php
@@ -25,7 +25,6 @@ public function name(): string
      * Resolve the field directive.
      *
      * @param  \Nuwave\Lighthouse\Schema\Values\FieldValue  $fieldValue
-     *
      * @return \Nuwave\Lighthouse\Schema\Values\FieldValue
      */
     public function resolveField(FieldValue $fieldValue): FieldValue
diff --git a/src/Schema/Directives/Fields/AuthDirective.php b/src/Schema/Directives/Fields/AuthDirective.php
index 33ef2fc718..5c09badf49 100644
--- a/src/Schema/Directives/Fields/AuthDirective.php
+++ b/src/Schema/Directives/Fields/AuthDirective.php
@@ -40,7 +40,6 @@ public function name(): string
      * Resolve the field directive.
      *
      * @param  \Nuwave\Lighthouse\Schema\Values\FieldValue  $fieldValue
-     *
      * @return \Nuwave\Lighthouse\Schema\Values\FieldValue
      */
     public function resolveField(FieldValue $fieldValue): FieldValue
diff --git a/src/Schema/Directives/Fields/CanDirective.php b/src/Schema/Directives/Fields/CanDirective.php
index 0c15aafa32..ba570c4cc8 100644
--- a/src/Schema/Directives/Fields/CanDirective.php
+++ b/src/Schema/Directives/Fields/CanDirective.php
@@ -34,11 +34,11 @@ public function name(): string
      */
     public function handleField(FieldValue $value, Closure $next): FieldValue
     {
-        $resolver = $value->getResolver();
+        $previousResolver = $value->getResolver();
 
         return $next(
             $value->setResolver(
-                function ($root, array $args, GraphQLContext $context, ResolveInfo $resolveInfo) use ($resolver) {
+                function ($root, array $args, GraphQLContext $context, ResolveInfo $resolveInfo) use ($previousResolver) {
                     $gate = app(Gate::class);
                     $gateArguments = $this->getGateArguments();
 
@@ -55,7 +55,7 @@ function (string $ability) use ($context, $gate, $gateArguments): void {
                         }
                     );
 
-                    return call_user_func_array($resolver, func_get_args());
+                    return call_user_func_array($previousResolver, func_get_args());
                 }
             )
         );
diff --git a/src/Schema/Directives/Fields/EventDirective.php b/src/Schema/Directives/Fields/EventDirective.php
index 61233e20b7..717e5e916c 100644
--- a/src/Schema/Directives/Fields/EventDirective.php
+++ b/src/Schema/Directives/Fields/EventDirective.php
@@ -6,15 +6,32 @@
 use Nuwave\Lighthouse\Schema\Values\FieldValue;
 use Nuwave\Lighthouse\Schema\Directives\BaseDirective;
 use Nuwave\Lighthouse\Support\Contracts\FieldMiddleware;
+use Illuminate\Contracts\Events\Dispatcher as EventsDispatcher;
 
 class EventDirective extends BaseDirective implements FieldMiddleware
 {
+    /**
+     * @var \Illuminate\Contracts\Events\Dispatcher
+     */
+    protected $eventsDispatcher;
+
+    /**
+     * Construct EventDirective.
+     *
+     * @param  \Illuminate\Contracts\Events\Dispatcher  $eventsDispatcher
+     * @return void
+     */
+    public function __construct(EventsDispatcher $eventsDispatcher)
+    {
+        $this->eventsDispatcher = $eventsDispatcher;
+    }
+
     /**
      * Name of the directive.
      *
      * @return string
      */
-    public function name():string
+    public function name(): string
     {
         return 'event';
     }
@@ -31,14 +48,20 @@ public function handleField(FieldValue $value, Closure $next): FieldValue
     {
         $eventBaseName = $this->directiveArgValue('fire') ?? $this->directiveArgValue('class');
         $eventClassName = $this->namespaceClassName($eventBaseName);
-        $resolver = $value->getResolver();
+        $previousResolver = $value->getResolver();
+
+        return $next(
+            $value->setResolver(
+                function () use ($previousResolver, $eventClassName) {
+                    $result = call_user_func_array($previousResolver, func_get_args());
 
-        return $next($value->setResolver(function () use ($resolver, $eventClassName) {
-            $args = func_get_args();
-            $value = call_user_func_array($resolver, $args);
-            event(new $eventClassName($value));
+                    $this->eventsDispatcher->dispatch(
+                        new $eventClassName($result)
+                    );
 
-            return $value;
-        }));
+                    return $result;
+                }
+            )
+        );
     }
 }
diff --git a/src/Schema/Directives/Fields/FirstDirective.php b/src/Schema/Directives/Fields/FirstDirective.php
index e352f0abc3..4ae44cb01f 100644
--- a/src/Schema/Directives/Fields/FirstDirective.php
+++ b/src/Schema/Directives/Fields/FirstDirective.php
@@ -25,7 +25,6 @@ public function name(): string
      * Resolve the field directive.
      *
      * @param  \Nuwave\Lighthouse\Schema\Values\FieldValue  $fieldValue
-     *
      * @return \Nuwave\Lighthouse\Schema\Values\FieldValue
      */
     public function resolveField(FieldValue $fieldValue): FieldValue
diff --git a/src/Schema/Directives/Fields/InjectDirective.php b/src/Schema/Directives/Fields/InjectDirective.php
index c1082dc50c..c69590b07c 100644
--- a/src/Schema/Directives/Fields/InjectDirective.php
+++ b/src/Schema/Directives/Fields/InjectDirective.php
@@ -48,12 +48,12 @@ public function handleField(FieldValue $value, Closure $next): FieldValue
             );
         }
 
-        $previousResolvers = $value->getResolver();
+        $previousResolver = $value->getResolver();
 
         return $next(
             $value->setResolver(
-                function ($rootValue, array $args, GraphQLContext $context, ResolveInfo $resolveInfo) use ($contextAttributeName, $argumentName, $previousResolvers) {
-                    return $previousResolvers(
+                function ($rootValue, array $args, GraphQLContext $context, ResolveInfo $resolveInfo) use ($contextAttributeName, $argumentName, $previousResolver) {
+                    return $previousResolver(
                         $rootValue,
                         Arr::add($args, $argumentName, data_get($context, $contextAttributeName)),
                         $context,
diff --git a/src/Schema/Directives/Fields/MethodDirective.php b/src/Schema/Directives/Fields/MethodDirective.php
index 2fd60d1f08..961c98fed6 100644
--- a/src/Schema/Directives/Fields/MethodDirective.php
+++ b/src/Schema/Directives/Fields/MethodDirective.php
@@ -24,7 +24,6 @@ public function name(): string
      * Resolve the field directive.
      *
      * @param  \Nuwave\Lighthouse\Schema\Values\FieldValue  $fieldValue
-     *
      * @return \Nuwave\Lighthouse\Schema\Values\FieldValue
      */
     public function resolveField(FieldValue $fieldValue): FieldValue
diff --git a/src/Schema/Factories/FieldFactory.php b/src/Schema/Factories/FieldFactory.php
index ce5de988a8..ea252cf0fc 100644
--- a/src/Schema/Factories/FieldFactory.php
+++ b/src/Schema/Factories/FieldFactory.php
@@ -23,11 +23,13 @@
 use Nuwave\Lighthouse\Support\Contracts\GraphQLContext;
 use Nuwave\Lighthouse\Support\Contracts\HasErrorBuffer;
 use Nuwave\Lighthouse\Support\Contracts\HasArgumentPath;
+use Nuwave\Lighthouse\Support\Contracts\ProvidesResolver;
 use Nuwave\Lighthouse\Support\Traits\HasResolverArguments;
 use Nuwave\Lighthouse\Support\Contracts\ArgFilterDirective;
 use Nuwave\Lighthouse\Support\Contracts\ArgDirectiveForArray;
 use Nuwave\Lighthouse\Support\Contracts\ArgValidationDirective;
 use Nuwave\Lighthouse\Support\Contracts\ArgTransformerDirective;
+use Nuwave\Lighthouse\Support\Contracts\ProvidesSubscriptionResolver;
 
 class FieldFactory
 {
@@ -83,17 +85,36 @@ class FieldFactory
      */
     protected $currentHandlerArgsOfArgDirectivesAfterValidationDirective = [];
 
+    /**
+     * @var \Nuwave\Lighthouse\Support\Contracts\ProvidesResolver
+     */
+    protected $providesResolver;
+
+    /**
+     * @var \Nuwave\Lighthouse\Support\Contracts\ProvidesSubscriptionResolver
+     */
+    protected $providesSubscriptionResolver;
+
     /**
      * @param  \Nuwave\Lighthouse\Schema\Factories\DirectiveFactory  $directiveFactory
      * @param  \Nuwave\Lighthouse\Schema\Factories\ArgumentFactory  $argumentFactory
      * @param  \Nuwave\Lighthouse\Support\Pipeline  $pipeline
+     * @param  \Nuwave\Lighthouse\Support\Contracts\ProvidesResolver  $providesResolver
+     * @param  \Nuwave\Lighthouse\Support\Contracts\ProvidesSubscriptionResolver  $providesSubscriptionResolver
      * @return void
      */
-    public function __construct(DirectiveFactory $directiveFactory, ArgumentFactory $argumentFactory, Pipeline $pipeline)
-    {
+    public function __construct(
+        DirectiveFactory $directiveFactory,
+        ArgumentFactory $argumentFactory,
+        Pipeline $pipeline,
+        ProvidesResolver $providesResolver,
+        ProvidesSubscriptionResolver $providesSubscriptionResolver
+    ) {
         $this->directiveFactory = $directiveFactory;
         $this->argumentFactory = $argumentFactory;
         $this->pipeline = $pipeline;
+        $this->providesResolver = $providesResolver;
+        $this->providesSubscriptionResolver = $providesSubscriptionResolver;
     }
 
     /**
@@ -104,14 +125,19 @@ public function __construct(DirectiveFactory $directiveFactory, ArgumentFactory
      */
     public function handle(FieldValue $fieldValue): array
     {
-        $this->fieldValue = $fieldValue;
         $fieldDefinitionNode = $fieldValue->getField();
 
-        // Get the initial resolver from the FieldValue
-        // This is either the webonyx default resolver or provided by a directive
-        if ($fieldResolver = $this->directiveFactory->createFieldResolver($fieldDefinitionNode)) {
-            $this->fieldValue = $fieldResolver->resolveField($fieldValue);
+        // Directives have the first priority for defining a resolver for a field
+        if ($resolverDirective = $this->directiveFactory->createFieldResolver($fieldDefinitionNode)) {
+            $this->fieldValue = $resolverDirective->resolveField($fieldValue);
+        } else {
+            $this->fieldValue = $fieldValue->setResolver(
+             $fieldValue->getParentName() === 'Subscription'
+                    ? $this->providesSubscriptionResolver->provideSubscriptionResolver($fieldValue)
+                    : $this->providesResolver->provideResolver($fieldValue)
+            );
         }
+
         $resolver = $this->fieldValue->getResolver();
 
         $argumentValues = $this->getArgumentValues();
diff --git a/src/Schema/ResolverProvider.php b/src/Schema/ResolverProvider.php
new file mode 100644
index 0000000000..db360938a9
--- /dev/null
+++ b/src/Schema/ResolverProvider.php
@@ -0,0 +1,49 @@
+<?php
+
+namespace Nuwave\Lighthouse\Schema;
+
+use Closure;
+use Illuminate\Support\Str;
+use GraphQL\Executor\Executor;
+use Nuwave\Lighthouse\Support\Utils;
+use Nuwave\Lighthouse\Schema\Values\FieldValue;
+use Nuwave\Lighthouse\Exceptions\DefinitionException;
+use Nuwave\Lighthouse\Support\Contracts\ProvidesResolver;
+
+class ResolverProvider implements ProvidesResolver
+{
+    /**
+     * Provide a field resolver in case no resolver directive is defined for a field.
+     *
+     * @param  \Nuwave\Lighthouse\Schema\Values\FieldValue  $fieldValue
+     * @return \Closure
+     *
+     * @throws \Nuwave\Lighthouse\Exceptions\DefinitionException
+     */
+    public function provideResolver(FieldValue $fieldValue): Closure
+    {
+        if ($fieldValue->parentIsRootType()) {
+            $resolverClass = Utils::namespaceClassname(
+                Str::studly($fieldValue->getFieldName()),
+                $fieldValue->defaultNamespacesForParent(),
+                function (string $class): bool {
+                    return method_exists($class, 'resolve');
+                }
+            );
+
+            if (! $resolverClass) {
+                throw new DefinitionException(
+                    "Could not locate a default resolver for the field {$fieldValue->getFieldName()}"
+                );
+            }
+
+            return Closure::fromCallable(
+                [app($resolverClass), 'resolve']
+            );
+        }
+
+        return Closure::fromCallable(
+            Executor::getDefaultFieldResolver()
+        );
+    }
+}
diff --git a/src/Schema/Values/FieldValue.php b/src/Schema/Values/FieldValue.php
index d90bf601c9..6eeb4280cd 100644
--- a/src/Schema/Values/FieldValue.php
+++ b/src/Schema/Values/FieldValue.php
@@ -3,21 +3,10 @@
 namespace Nuwave\Lighthouse\Schema\Values;
 
 use Closure;
-use Illuminate\Support\Str;
-use GraphQL\Executor\Executor;
 use GraphQL\Type\Definition\Type;
-use Nuwave\Lighthouse\Support\Utils;
-use GraphQL\Type\Definition\ResolveInfo;
 use GraphQL\Language\AST\StringValueNode;
-use Nuwave\Lighthouse\Schema\AST\ASTHelper;
 use GraphQL\Language\AST\FieldDefinitionNode;
-use Nuwave\Lighthouse\Subscriptions\Subscriber;
-use Nuwave\Lighthouse\Exceptions\DefinitionException;
-use Nuwave\Lighthouse\Schema\Types\GraphQLSubscription;
-use Nuwave\Lighthouse\Support\Contracts\GraphQLContext;
-use Nuwave\Lighthouse\Subscriptions\SubscriptionRegistry;
 use Nuwave\Lighthouse\Schema\Conversion\DefinitionNodeConverter;
-use Nuwave\Lighthouse\Subscriptions\Exceptions\UnauthorizedSubscriber;
 
 class FieldValue
 {
@@ -169,118 +158,11 @@ public function getField(): FieldDefinitionNode
      *
      * @return \Closure
      */
-    public function getResolver(): Closure
+    public function getResolver(): ?Closure
     {
-        if (! isset($this->resolver)) {
-            $this->resolver = $this->defaultResolver();
-        }
-
         return $this->resolver;
     }
 
-    /**
-     * Get default field resolver.
-     *
-     * @return \Closure
-     *
-     * @throws \Nuwave\Lighthouse\Exceptions\DefinitionException
-     */
-    protected function defaultResolver(): Closure
-    {
-        if ($this->getParentName() === 'Subscription') {
-            return $this->defaultSubscriptionResolver();
-        }
-
-        if ($this->parentIsRootType()) {
-            $resolverClass = Utils::namespaceClassname(
-                Str::studly($this->getFieldName()),
-                $this->defaultNamespacesForParent(),
-                function (string $class): bool {
-                    return method_exists($class, 'resolve');
-                }
-            );
-
-            if (! $resolverClass) {
-                throw new DefinitionException(
-                    "Could not locate a default resolver for the field {$this->field->name->value}"
-                );
-            }
-
-            return Closure::fromCallable(
-                [app($resolverClass), 'resolve']
-            );
-        }
-
-        return Closure::fromCallable(
-            [Executor::class, 'defaultFieldResolver']
-        );
-    }
-
-    /**
-     * Get the default resolver for a subscription field.
-     *
-     * @return \Closure
-     *
-     * @throws \Nuwave\Lighthouse\Exceptions\DefinitionException
-     */
-    protected function defaultSubscriptionResolver(): Closure
-    {
-        if ($directive = ASTHelper::directiveDefinition($this->field, 'subscription')) {
-            $className = ASTHelper::directiveArgValue($directive, 'class');
-        } else {
-            $className = Str::studly($this->getFieldName());
-        }
-
-        $className = Utils::namespaceClassname(
-            $className,
-            $this->defaultNamespacesForParent(),
-            function (string $class): bool {
-                return is_subclass_of($class, GraphQLSubscription::class);
-            }
-        );
-
-        if (! $className) {
-            throw new DefinitionException(
-                "No class found for the subscription field {$this->getFieldName()}"
-            );
-        }
-
-        /** @var \Nuwave\Lighthouse\Schema\Types\GraphQLSubscription $subscription */
-        $subscription = app($className);
-        /** @var \Nuwave\Lighthouse\Subscriptions\SubscriptionRegistry $subscriptionRegistry */
-        $subscriptionRegistry = app(SubscriptionRegistry::class);
-
-        // Subscriptions can only be placed on a single field on the root
-        // query, so there is no need to consider the field path
-        $subscriptionRegistry->register(
-            $subscription,
-            $this->getFieldName()
-        );
-
-        return function ($root, array $args, GraphQLContext $context, ResolveInfo $resolveInfo) use ($subscription, $subscriptionRegistry) {
-            if ($root instanceof Subscriber) {
-                return $subscription->resolve($root->root, $args, $context, $resolveInfo);
-            }
-
-            $subscriber = new Subscriber(
-                $args,
-                $context,
-                $resolveInfo
-            );
-
-            if (! $subscription->can($subscriber)) {
-                throw new UnauthorizedSubscriber(
-                    'Unauthorized subscription request'
-                );
-            }
-
-            $subscriptionRegistry->subscriber(
-                $subscriber,
-                $subscription->encodeTopic($subscriber, $this->getFieldName())
-            );
-        };
-    }
-
     /**
      * Return the namespaces configured for the parent type.
      *
@@ -339,7 +221,7 @@ public function getDeprecationReason(): ?string
      *
      * @return bool
      */
-    protected function parentIsRootType(): bool
+    public function parentIsRootType(): bool
     {
         return in_array(
             $this->getParentName(),
diff --git a/src/Subscriptions/SubscriptionResolverProvider.php b/src/Subscriptions/SubscriptionResolverProvider.php
new file mode 100644
index 0000000000..d837390bd4
--- /dev/null
+++ b/src/Subscriptions/SubscriptionResolverProvider.php
@@ -0,0 +1,100 @@
+<?php
+
+namespace Nuwave\Lighthouse\Subscriptions;
+
+use Closure;
+use Illuminate\Support\Str;
+use Nuwave\Lighthouse\Support\Utils;
+use GraphQL\Type\Definition\ResolveInfo;
+use Nuwave\Lighthouse\Schema\AST\ASTHelper;
+use Nuwave\Lighthouse\Schema\Values\FieldValue;
+use Nuwave\Lighthouse\Exceptions\DefinitionException;
+use Nuwave\Lighthouse\Schema\Types\GraphQLSubscription;
+use Nuwave\Lighthouse\Support\Contracts\GraphQLContext;
+use Nuwave\Lighthouse\Support\Contracts\ProvidesSubscriptionResolver;
+use Nuwave\Lighthouse\Subscriptions\Exceptions\UnauthorizedSubscriber;
+
+class SubscriptionResolverProvider implements ProvidesSubscriptionResolver
+{
+    /**
+     * @var \Nuwave\Lighthouse\Subscriptions\SubscriptionRegistry
+     */
+    protected $subscriptionRegistry;
+
+    /**
+     * ResolverProvider constructor.
+     *
+     * @param  \Nuwave\Lighthouse\Subscriptions\SubscriptionRegistry  $subscriptionRegistry
+     * @return void
+     */
+    public function __construct(SubscriptionRegistry $subscriptionRegistry)
+    {
+        $this->subscriptionRegistry = $subscriptionRegistry;
+    }
+
+    /**
+     * Provide a field resolver in case no resolver directive is defined for a field.
+     *
+     * @param  \Nuwave\Lighthouse\Schema\Values\FieldValue  $fieldValue
+     * @return \Closure
+     *
+     * @throws \Nuwave\Lighthouse\Exceptions\DefinitionException
+     */
+    public function provideSubscriptionResolver(FieldValue $fieldValue): Closure
+    {
+        $fieldName = $fieldValue->getFieldName();
+
+        if ($directive = ASTHelper::directiveDefinition($fieldValue->getField(), 'subscription')) {
+            $className = ASTHelper::directiveArgValue($directive, 'class');
+        } else {
+            $className = Str::studly($fieldName);
+        }
+
+        $className = Utils::namespaceClassname(
+            $className,
+            $fieldValue->defaultNamespacesForParent(),
+            function (string $class): bool {
+                return is_subclass_of($class, GraphQLSubscription::class);
+            }
+        );
+
+        if (! $className) {
+            throw new DefinitionException(
+                "No class found for the subscription field {$fieldName}"
+            );
+        }
+
+        /** @var \Nuwave\Lighthouse\Schema\Types\GraphQLSubscription $subscription */
+        $subscription = app($className);
+
+        // Subscriptions can only be placed on a single field on the root
+        // query, so there is no need to consider the field path
+        $this->subscriptionRegistry->register(
+            $subscription,
+            $fieldName
+        );
+
+        return function ($root, array $args, GraphQLContext $context, ResolveInfo $resolveInfo) use ($subscription, $fieldName) {
+            if ($root instanceof Subscriber) {
+                return $subscription->resolve($root->root, $args, $context, $resolveInfo);
+            }
+
+            $subscriber = new Subscriber(
+                $args,
+                $context,
+                $resolveInfo
+            );
+
+            if (! $subscription->can($subscriber)) {
+                throw new UnauthorizedSubscriber(
+                    'Unauthorized subscription request'
+                );
+            }
+
+            $this->subscriptionRegistry->subscriber(
+                $subscriber,
+                $subscription->encodeTopic($subscriber, $fieldName)
+            );
+        };
+    }
+}
diff --git a/src/Subscriptions/SubscriptionServiceProvider.php b/src/Subscriptions/SubscriptionServiceProvider.php
index 0a6c4e45aa..5414ee9cb7 100644
--- a/src/Subscriptions/SubscriptionServiceProvider.php
+++ b/src/Subscriptions/SubscriptionServiceProvider.php
@@ -12,6 +12,7 @@
 use Nuwave\Lighthouse\Subscriptions\Contracts\ContextSerializer;
 use Nuwave\Lighthouse\Subscriptions\Contracts\StoresSubscriptions;
 use Nuwave\Lighthouse\Subscriptions\Contracts\SubscriptionIterator;
+use Nuwave\Lighthouse\Support\Contracts\ProvidesSubscriptionResolver;
 use Nuwave\Lighthouse\Support\Contracts\SubscriptionExceptionHandler;
 use Nuwave\Lighthouse\Subscriptions\Contracts\AuthorizesSubscriptions;
 use Nuwave\Lighthouse\Subscriptions\Contracts\BroadcastsSubscriptions;
@@ -72,5 +73,6 @@ public function register(): void
         $this->app->bind(SubscriptionIterator::class, SyncIterator::class);
         $this->app->bind(SubscriptionExceptionHandler::class, ExceptionHandler::class);
         $this->app->bind(BroadcastsSubscriptions::class, SubscriptionBroadcaster::class);
+        $this->app->bind(ProvidesSubscriptionResolver::class, SubscriptionResolverProvider::class);
     }
 }
diff --git a/src/Support/Contracts/ProvidesResolver.php b/src/Support/Contracts/ProvidesResolver.php
new file mode 100644
index 0000000000..fa7ed3206e
--- /dev/null
+++ b/src/Support/Contracts/ProvidesResolver.php
@@ -0,0 +1,17 @@
+<?php
+
+namespace Nuwave\Lighthouse\Support\Contracts;
+
+use Closure;
+use Nuwave\Lighthouse\Schema\Values\FieldValue;
+
+interface ProvidesResolver
+{
+    /**
+     * Provide a field resolver in case no resolver directive is defined for a field.
+     *
+     * @param  \Nuwave\Lighthouse\Schema\Values\FieldValue  $fieldValue
+     * @return \Closure
+     */
+    public function provideResolver(FieldValue $fieldValue): Closure;
+}
diff --git a/src/Support/Contracts/ProvidesSubscriptionResolver.php b/src/Support/Contracts/ProvidesSubscriptionResolver.php
new file mode 100644
index 0000000000..196cd146ca
--- /dev/null
+++ b/src/Support/Contracts/ProvidesSubscriptionResolver.php
@@ -0,0 +1,17 @@
+<?php
+
+namespace Nuwave\Lighthouse\Support\Contracts;
+
+use Closure;
+use Nuwave\Lighthouse\Schema\Values\FieldValue;
+
+interface ProvidesSubscriptionResolver
+{
+    /**
+     * Provide a field resolver for subscriptions.
+     *
+     * @param  \Nuwave\Lighthouse\Schema\Values\FieldValue  $fieldValue
+     * @return \Closure
+     */
+    public function provideSubscriptionResolver(FieldValue $fieldValue): Closure;
+}
diff --git a/tests/Integration/CustomDefaultResolverTest.php b/tests/Integration/CustomDefaultResolverTest.php
new file mode 100644
index 0000000000..41bb8b544d
--- /dev/null
+++ b/tests/Integration/CustomDefaultResolverTest.php
@@ -0,0 +1,56 @@
+<?php
+
+namespace Tests\Integration;
+
+use Tests\TestCase;
+use GraphQL\Executor\Executor;
+
+class CustomDefaultResolverTest extends TestCase
+{
+    const CUSTOM_RESOLVER_RESULT = 123;
+
+    protected $schema = '
+    type Query {
+        foo: Foo @field(resolver: "Tests\\\\Integration\\\\CustomDefaultResolverTest@resolve")
+    }
+    
+    type Foo {
+        bar: Int
+    }
+    ';
+
+    public function resolve(): array
+    {
+        return [
+            'bar' => 'This should not be returned.',
+        ];
+    }
+
+    /**
+     * @test
+     */
+    public function itCanSpecifyACustomDefaultResolver(): void
+    {
+        $previous = Executor::getDefaultFieldResolver();
+
+        Executor::setDefaultFieldResolver(function (): int {
+            return self::CUSTOM_RESOLVER_RESULT;
+        });
+
+        $this->query('
+        {
+            foo {
+                bar
+            }
+        }
+        ')->assertJson([
+            'data' => [
+                'foo' => [
+                    'bar' => self::CUSTOM_RESOLVER_RESULT,
+                ],
+            ],
+        ]);
+
+        Executor::setDefaultFieldResolver($previous);
+    }
+}
diff --git a/tests/Unit/Schema/Values/FieldValueTest.php b/tests/Unit/Schema/ResolverProviderTest.php
similarity index 70%
rename from tests/Unit/Schema/Values/FieldValueTest.php
rename to tests/Unit/Schema/ResolverProviderTest.php
index 97cce1a46b..dd4908f68b 100644
--- a/tests/Unit/Schema/Values/FieldValueTest.php
+++ b/tests/Unit/Schema/ResolverProviderTest.php
@@ -1,16 +1,29 @@
 <?php
 
-namespace Tests\Unit\Schema\Values;
+namespace Tests\Unit\Schema;
 
 use Closure;
 use Tests\TestCase;
+use Nuwave\Lighthouse\Schema\ResolverProvider;
 use Nuwave\Lighthouse\Schema\Values\NodeValue;
 use Nuwave\Lighthouse\Schema\AST\PartialParser;
 use Nuwave\Lighthouse\Schema\Values\FieldValue;
 use Nuwave\Lighthouse\Exceptions\DefinitionException;
 
-class FieldValueTest extends TestCase
+class ResolverProviderTest extends TestCase
 {
+    /**
+     * @var \Nuwave\Lighthouse\Schema\ResolverProvider
+     */
+    private $resolverProvider;
+
+    protected function setUp(): void
+    {
+        parent::setUp();
+
+        $this->resolverProvider = new ResolverProvider();
+    }
+
     /**
      * @test
      */
@@ -20,7 +33,7 @@ public function itGetsTheWebonyxDefaultResolverForNonRootFields(): void
 
         $this->assertInstanceOf(
             Closure::class,
-            $fieldValue->getResolver()
+            $this->resolverProvider->provideResolver($fieldValue)
         );
     }
 
@@ -33,7 +46,7 @@ public function itGetsTheConventionBasedDefaultResolverForRootFields(): void
 
         $this->assertInstanceOf(
             Closure::class,
-            $fieldValue->getResolver()
+            $this->resolverProvider->provideResolver($fieldValue)
         );
     }
 
@@ -46,7 +59,7 @@ public function itLooksAtMultipleNamespacesWhenLookingForDefaultFieldResolvers()
 
         $this->assertInstanceOf(
             Closure::class,
-            $fieldValue->getResolver()
+            $this->resolverProvider->provideResolver($fieldValue)
         );
     }
 
@@ -57,7 +70,9 @@ public function itThrowsIfRootFieldHasNoResolver(): void
     {
         $this->expectException(DefinitionException::class);
 
-        $this->constructFieldValue('noFieldClass: Int')->getResolver();
+        $this->resolverProvider->provideResolver(
+            $this->constructFieldValue('noFieldClass: Int')
+        );
     }
 
     protected function constructFieldValue(string $fieldDefinition, string $parentTypeName = 'Query'): FieldValue