使用Laravel和Lighthouse快速构建GraphQL服务器

首先

我们将使用Laravel的GraphQL框架”Lighthouse”构建GraphQL服务器。

 

另外,我们将使用Postman或Next.js的Apollo Client来实际执行查询请求来测试所构建的GraphQL服务器。

GraphQL是一种查询语言。

GraphQL是一种用于API的查询语言,用于执行查询并使用现有数据的运行时。
https://graphql.org/

根据公式的解释,GraphQL是一种用于API的查询语言。
通过向一个端点发送请求,GraphQL可以获取和更新数据。

index-diagram (1).png

画像来源:《Apollo Server 简介》

以下是特点,包括以下优点和缺点。

优点

    • 型を指定できる

 

    • RESTの問題点(オーバーフェッチ、アンダーフェッチ)を解消できる

必要な情報のみ取得できる
複数の情報を少ないリクエストで取得できる

缺点

    • N+1問題が発生する

 

    • HTTPキャッシュ方式をサポートしていない

 

    クエリが複雑化する

关于GraphQL的问题,您可以参考这篇文章。

 

环境

    • PHP 8.2.0

 

    • Laravel 9.43.0

 

    • nuwave/lighthouse 5.68

 

    • React 18.2.0

 

    • Next.js 13.0.6

 

    Apollo Client 3.7.2

建立GraphQL服务器

安装Lighthouse

现在我们将按照官方文件的指引来构建GraphQL服务器。

首先,我们要安装包。

$ composer require nuwave/lighthouse

接下来,我们将创建一个架构定义文件。

$ php artisan vendor:publish --tag=lighthouse-schema

将在/graphql目录下创建schema.graphql文件。
您将在该文件中编写模式定义(默认情况下已定义了用户模式)。

scalar DateTime
    @scalar(class: "Nuwave\\Lighthouse\\Schema\\Types\\Scalars\\DateTime")

type Query {
    user(
        id: ID @eq @rules(apply: ["prohibits:email", "required_without:email"])

        email: String
            @eq
            @rules(apply: ["prohibits:id", "required_without:id", "email"])
    ): User @find

    users(
        name: String @where(operator: "like")
    ): [User!]! @paginate(defaultCount: 10)
}

type User {
    id: ID!
    name: String!
    email: String!
    email_verified_at: DateTime
    created_at: DateTime!
    updated_at: DateTime!
}
关于@eq和@rules被称为目录,在@rules中,例如可以定义验证。

为了通过/graphql端点发送请求,需要进行cors设置。

return [
-     'paths' => ['api/*', 'sanctum/csrf-cookie'],
+     'paths' => ['api/*', 'graphql', 'sanctum/csrf-cookie'],
    ...

创建测试数据

在工厂预先创建测试数据。

用户表的模式
公共函数向上执行()
{
架构::创建(‘用户’, 函数 (蓝图 $表) {
$表->标识符();
$表->字符串(‘姓名’);
$表->字符串(‘电子邮件’)->唯一();
$表->时间戳(‘电子邮件验证时间’)->可为空();
$表->字符串(‘密码’);
$表->记住令牌();
$表->时间戳();
});
}
发布表的模式
public function up()
{
Schema::create(‘posts’, function (Blueprint $table) {
$table->id();
$table->foreignIdFor(User::class)->constrained();
$table->text(‘contents’);
$table->timestamps();
});
}
UserFactory.phpUserFactory.php
public function definition()
{
return [
‘name’ => $this->faker->name(),
’email’ => $this->faker->unique()->safeEmail(),
’email_verified_at’ => now(),
‘password’ => ‘$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi’, // 密码
‘remember_token’ => Str::random(10),
];
}

PostFacotry.phpPostFactory.php
公共函数定义()
{
返回 [
‘用户ID’ => 1,
‘内容’ => $this->faker->realText(32),
];
}

DatabaseSeeder.phpDatabaseSeeder.php
public function run()
{
User::factory(1)->create();
Post::factory(10)->create();
}

数据库种子文件(DatabaseSeeder.php)

数据库种子文件(DatabaseSeeder.php)
public function run()
{
User::factory(1)->create();
Post::factory(10)->create();
}

试着从邮递员那里发送查询。

那么我们来尝试向GraphQL的端点发送查询吧。

HTTP请求方法需要使用POST。
スクリーンショット 0004-12-16 12.43.14.png

让我们也尝试从Next.js发送查询

我将使用Next.js搭建项目并安装Apollo Client。

$ npx create-next-app --ts
$ yarn add @apollo/client graphql

我将修改index.tsx文件,将获取到的数据打印到控制台上。

import { ApolloClient, InMemoryCache, gql } from '@apollo/client';

const client = new ApolloClient({

  uri: 'http://localhost:9004/graphql',
  cache: new InMemoryCache(),
});

export default function Home() {
  client
    .query({
      query: gql`
        query GetUsers {
          user(id: 1) {
            name
            email
          }
        }
      `,
    })
    .then((result) => console.log(result));
    ...
スクリーンショット 0004-12-16 14.31.31.png

获取相关数据部分同时进行整合

让我们一起获取已经设置好的相关数据。
首先,我们需要定义模型的相关关系。

public function posts(): HasMany
{
    return $this->hasMany(Post::class);
}
public function user(): BelongsTo
{
    return $this->belongsTo(User::class);
}

将Post的定义添加到GraphQL模式中。

type User {
    id: ID!
    name: String!
    email: String!
    email_verified_at: DateTime
+   posts: [Post]
    created_at: DateTime!
    updated_at: DateTime!
}

+ type Post {
+    id: ID!
+    user_id: Int!
+    contents: String!
+    created_at: DateTime!
+    updated_at: DateTime!
+ }
スクリーンショット 0004-12-16 14.20.09.png

执行Mutation(更新系的查询)

我将尝试在Mutation中添加一条记录。
首先,在GraphQL模式中添加Mutation的定义。

type Mutation {
    createPost(
        user_id: Int
        contents: String
    ): Post
        @create(model: Post)
}
需要将运行时的查询更改为变更。
スクリーンショット 0004-12-16 14.27.33.png

这个帖子里已经插入了记录。

解决N+1问题

在开始部分,提到了GraphQL的缺点之一是”N+1问题”。
如果按照上述关联数据一起获取的方式去做,那么每个帖子都会发出查询请求。

通过在Lighthouse中使用@hasMany或@belongsTo,可以告诉它通过Eager loading来解决这个问题。

type User {
    id: ID!
    name: String!
    email: String!
    email_verified_at: DateTime
-   posts: [Post]
+   posts: [Post] @hasMany
    created_at: DateTime!
    updated_at: DateTime!
}

总结

您还可以定义页面分页,使用本地作用域,并使用Eloquant进行各种功能的协作。请详细参阅官方文档。

最后

GoQSystem目前正在招募一起工作的伙伴!

如果你有兴趣,请通过以下链接进行确认。

 

广告
将在 10 秒后关闭
bannerAds