# Fields

The entrypoints to any GraphQL API are the fields of the root types Query, Mutation and Subscription.

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.

# Hello World

As is the tradition of our people, this section will teach you how to say "hello world!" through Lighthouse.

We start out by defining the simplest possible schema: The root Query type with a single field called hello that returns a String.

type Query {
  hello: String!
}

This defines the shape of our data and informs the client what they can expect. You need to implement the actual resolver next.

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 __invoke function with the usual resolver arguments.

In this case, our field is a query and is called hello, so we need to define our class as follows:

namespace App\GraphQL\Queries;

final class Hello
{
    public function __invoke(): string
    {
        return 'world!';
    }
}

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.

{
  hello
}

This query will return the following response:

{
  "data": {
    "hello": "world!"
  }
}

# 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.

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.

namespace App\GraphQL\Queries;

final class Greet
{
    public function __invoke(mixed $root, array $args): string
    {
        return "Hello, {$args['name']}!";
    }
}

We can call this query, passing a name of our choosing.

{
  greet(name: "Foo")
}

And receive a friendly greeting.

{
  "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.

type Query {
  greet(name: String = "you"): String
}

Now we can use our query like this:

{
  greet
}
{
  "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?

type Query {
  user: User!
}

type User {
  id: ID!
  name: String!
  email: String
}

Let's play through what happens when the client sends the following query:

{
  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 already resolved the User in the parent field, we do not want to fetch it again to get its 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:

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.

use App\Models\User;
use Nuwave\Lighthouse\Execution\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 (opens new window), 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 create a resolver class using the built-in artisan command lighthouse:field. For example, this is how you generate a class for the field name on type User:

php artisan lighthouse:field User.name

# Resolver precedence

Lighthouse uses the following logic to locate field resolvers.

First, it checks if the field definition in the schema is annotated with a FieldResolver directive. If so, it uses the resolver that is provided by the directive.

The interface \Nuwave\Lighthouse\Support\Contracts\ProvidesResolver (opens new window) is expected to provide a resolver in case no resolver directive is defined for a field. When the field is defined on the root Subscription type, the Nuwave\Lighthouse\Support\Contracts\ProvidesSubscriptionResolver (opens new window) interface is used instead.

The default implementation of those interfaces check if there is a class that matches the capitalized name of the field, and defines a method __invoke, in one of the configured default namespaces, depending on the parent type the field is a part of.

Parent type Namespaces config key
Query lighthouse.namespaces.queries
Mutation lighthouse.namespaces.mutations
Subscription lighthouse.namespaces.subscriptions
Any other type lighthouse.namespaces.types

For example, given lighthouse.namespaces.queries is defined as 'App\GraphQL\Queries' and the field Query.users, Lighthouse would expect a resolver class App\GraphQL\Queries\User. If there are multiple namespaces, they are checked in order and the first found class is used.

Non-root types are combined with the configured namespaces. For example, given lighthouse.namespaces.types is defined as ['App\GraphQL\Types', 'MyModule\GraphQL\Types'] and the field User.name, Lighthouse would look for the resolver class App\GraphQL\Types\User\Name, then MyModule\GraphQL\Types\User\Name.

Resolvers for root fields are mandatory, Lighthouse will throw during schema validation if a root field has no resolver. Non-root fields fall back to webonyx's default resolver (opens new window).