laravelgraphqllaravel-lighthouse

Can i create custom query for @find directive with nuwave/lighthouse 6.6?


In laravel 9.48 / nuwave/lighthouse 6.6 app I need getting single row with @find directive to add some additive fields which I do not have in database ,but calculate “on fly” published_label based on published and created_at_formatted and created_at_formatted based on created_at :

{
  article(id:1)  {
     id
     title
     text
     text_shortly
     creator {
        id
        username
        email
        status
     }
      published
      published_label
      created_at_formatted 
      created_at
  }
}

I tried to create custom query, like I do with custom query for @paginate( directiver, which has possibility to use custom resolver and withc command

php artisan lighthouse:query Article

new file app/GraphQL/Queries/Article.php was created.

and modified graphql file :

extend type Query {
    articles(
        filterByPublished: Boolean @where(key: "filterByPublished")
        filterByTitle: String @where(key: "filterByTitle")
        sortedBy: String @where(key: "sortedBy")
        page: Int @where(key: "page")
        count: Int @where(key: "count")
    ): [Article!]! @paginate(defaultCount: 10, resolver: "App\\GraphQL\\Queries\\Builders\\CustomArticles@index")

#    article(id: ID! @eq): Article @find
    article(id: ID! @eq): Article @find(resolver: "App\\GraphQL\\Queries\\Article")
}

Command did not raise any errors

php artisan lighthouse:validate-schema

and my request returned error :

"Cannot return null for non-nullable field \"Article.published_label\"

looks like my custom query was not called.

Reading docs at https://lighthouse-php.com/6/api-reference/directives.html#find I did not fond any resolver mentioned here... How can I make it ?


Solution

  • No need to create a custom @find directive here.

    I can see two ways to achieve what you want:

    1. Create a the "published_label" attribute on your Article model, the quick and dirty way :
    public function publishedLabel(): Attribute
    {
      return Attribute::get(fn () => "Published since " . $this->published_at->format(...));
    }
    
    1. Create a dedicated resolver, the clean way IMHO :
    namespace App\GraphQL\Resolvers\Types\Article;
    use App\Models\Article;
    
    class PublishedLabel {
      public function __invoke(Article $parent): string {
        return "Published since " . $parent->published_at->format(...);
      }
    }
    
    // then in your graphql definition
    type Article {
      ...
      published: Datetime!
      published_label: String! @field(resolver: "App\\GraphQL\\Resolvers\\Types\\Article\\PublishedLabel")
    }
    

    Read more about this here : https://lighthouse-php.com/master/api-reference/directives.html#field

    Bonus

    With this second way, you can even imagine some arguments, such as "locale", "format" (short/long), ... for endless possibilities

    type Article {
      ...
      published: Datetime!
      published_label(locale: String = "en"): String! @field(resolver: "App\\GraphQL\\Resolvers\\Types\\Article\\PublishedLabel")
    }
    
    namespace App\GraphQL\Resolvers\Types\Article;
    use App\Models\Article;
    
    class PublishedLabel {
      public function __invoke(Article $parent, $args): string {
        $locale = $args['locale'];
        $publishedAt = $parent->published_at;
    
        return ....;
      }
    }