Phần 1: Mutation GraphQL

Trong bài viết trước, chúng ta đã học cách xây dựng API xây dựng sử dụng Laravel và GraphQL cho danh sách product và user, nếu bạn chưa đọc thì bạn nên đọc nó trước. Còn bây giờ chúng ta sẽ tìm hiểu về Mutation và Authentication API với GraphQL.

GraphQL là công nghệ tuyệt vời để tạo ra API đơn giản và linh hoạt, nó mạnh mẽ cho client app phát triển bằng React Native / ReactJs. Có rất nhiều thư viện để hỗ trợ cho GraphQL và được giới thiệu tại đây.

Tạo class Mutation

Mutation là kiểu để insert dữ liệu vào hoặc thay đổi dữ liệu đã có trong database. Bạn có thể xem chi tiết hơn tại đây. Trong bài trước chúng ta đã có User Model, trong bài này này thì chúng ta sẽ thêm mới user và update thông tin user bằng cách sử dụng mutation query. Đầu tiên chúng ta phải thêm class mới cho create user và update user.

Trong NewUserMutation.php chúng ta có function args() để declare các field và type của input. Type :: nonNull có nghĩa là field này bắt buộc. Trong GraphQL, query được đánh dấu bởi dấu chấm than (!) và sau khi save thành công thì chúng ta sẽ return về user data.

<?php
/**
 * Created by PhpStorm.
 * User: ardani
 * Date: 8/4/17
 * Time: 10:02 AM
 */
namespace AppGraphQLMutation;
use GraphQLTypeDefinitionType;
use RebingGraphQLSupportFacadesGraphQL;
use RebingGraphQLSupportMutation;
use AppUser;

class NewUserMutation extends Mutation
{
    protected $attributes = [
        'name' => 'NewUser'
    ];
    
    public function type()
    {
        return GraphQL::type('users');
    }
    
    public function args()
    {
        return [
            'name' => [
                'name' => 'name',
                'type' => Type::nonNull(Type::string())
            ],
            'email' => [
                'name' => 'email',
                'type' => Type::nonNull(Type::string())
            ],
            'password' => [
                'name' => 'password',
                'type' => Type::nonNull(Type::string())
            ],
            'first_name' => [
                'name' => 'first_name',
                'type' => Type::nonNull(Type::string())
            ],
            'last_name' => [
                'name' => 'last_name',
                'type' => Type::string()
            ],
            'avatar' => [
                'name' => 'avatar',
                'type' => Type::string()
            ]
        ];
    }
    
    public function resolve($root, $args)
    {
        $args['password'] = bcrypt($args['password']);
        $user = User::create($args);
        if (!$user) {
            return null;
        }
        
        $user->user_profiles()->create($args);
        
        return $user;
    }
}

UpdateUserMutation.php là ví dụ về update user data, tất cả các hoạt động update và insert vẫn sử dụng Eloquent và bạn có thể sử dụng Query builder cho query phức tạp.

<?php
/**
 * Created by PhpStorm.
 * User: ardani
 * Date: 8/4/17
 * Time: 10:02 AM
 */
namespace AppGraphQLMutation;
use GraphQLTypeDefinitionType;
use RebingGraphQLSupportFacadesGraphQL;
use RebingGraphQLSupportMutation;
use AppUser;

class UpdateUserMutation extends Mutation
{
    protected $attributes = [
        'name' => 'UpdateUser'
    ];
    
    public function type()
    {
        return GraphQL::type('users');
    }
    
    public function args()
    {
        return [
            'id' => [
                'name' => 'id',
                'type' => Type::nonNull(Type::int())
            ],
            'name' => [
                'name' => 'name',
                'type' => Type::nonNull(Type::string())
            ]
        ];
    }
    
    public function resolve($root, $args)
    {
        $user = User::find($args['id']);
        if (!$user) {
            return null;
        }
        
        $user->name = $args['name'];
        $user->save();
        
        return $user;
    }
}

Append Mutation vào GraphQL

Bạn có thể tìm thấy graphql.php trong thư mục config và thêm class với namespace vào trong mutation schema.

Demo

alt

Phần 2: JWT Authentication GraphQL

JSON Web Token (JWT) là một open standard (RFC 7519) định nghĩa cách thức truyền tin an toàn giữa các thành viên bằng 1 đối tượng JSON . Thông tin này có thể được xác thực và đánh dấu tin cậy bởi “chữ ký” của nó. Phần chữ ký của JWT sẽ được mã hóa lại bằng HMAC hoặc RSA.

Install JWT Package

JWT rất hữu ích cho việc xác thực API Stateless và dễ dàng tích hợp với Laravel khi sử dụng package này.
Cài đặt JWT, bạn có thể làm theo hướng dẫn ở đây và để xử lý lỗi thì ở đây.

Create Controller For Authentication

Chúng ta sử dụng header param Authorization: Bearer {yourtokenhere} với mọi request đối với GraphQL. Đầu tiên ta tạo controller cho authentication như dưới đây:

<?php
/**
 * Created by PhpStorm.
 * User: ardani
 * Date: 8/4/17
 * Time: 11:18 AM
 */
namespace AppHttpControllers;
use IlluminateHttpRequest;
use TymonJWTAuthExceptionsJWTException;
use TymonJWTAuthJWTAuth;

class AuthenticateController extends Controller
{
    private $jwt;
    
    public function __construct(JWTAuth $jwt)
    {
        $this->jwt = $jwt;
    }
    
    public function authenticate(Request $request)
    {
        // grab credentials from the request
        $credentials = $request->only('email', 'password');
        
        try {
            // attempt to verify the credentials and create a token for the user
            if (! $token = $this->jwt->attempt($credentials)) {
                return response()->json(['error' => 'invalid_credentials'], 401);
            }
        } catch (JWTException $e) {
            // something went wrong whilst attempting to encode the token
            return response()->json(['error' => 'could_not_create_token'], 500);
        }
        
        // all good so return the token
        return response()->json(compact('token'));
    }
}

Thêm mới endpoint trong web.php cho login:
Route::post(‘graphql/login’, ‘AuthenticateController@authenticate’);

Create Query With Authentication

Ví dụ chúng ta sử dụng query myprofile và truy cập vào myprofile cần phải có authentication từ JWT. Đầu tiên chúng ta tạo MyProfileQuery.php:

<?php
namespace AppGraphQLQuery;

use AppUser;
use RebingGraphQLSupportFacadesGraphQL;
use RebingGraphQLSupportQuery;
use RebingGraphQLSupportSelectFields;
use TymonJWTAuthFacadesJWTAuth;

class MyProfileQuery extends Query
{
    private $auth;
    protected $attributes = [
        'name' => 'My Profile Query',
        'description' => 'My Profile Information'
    ];
    
    public function authorize(array $args)
    {
        try {
            $this->auth = JWTAuth::parseToken()->authenticate();
        } catch (Exception $e) {
            $this->auth = null;
        }
        
        return (boolean) $this->auth;
    }
    
    public function type()
    {
        return GraphQL::type('myprofile');
    }
    
    public function resolve($root, $args, SelectFields $fields)
    {
        $user = User::with(array_keys($fields->getRelations()))
            ->where('id', $this->auth->id)
            ->select($fields->getSelect())->first();
            
        return $user;
    }
}

Quá trình xác thực xảy ra trong function authorize(), trong đó chúng ta phải kiểm tra JWT auth là hợp lệ để tiếp tục quá trình, nhưng nếu xác thực không hợp lệ sẽ hiển thị thông báo trái phép ở kết quả của GraphQ. Và đừng quên thêm vào truy vấn mới trong config graphql.php:

Demo

alt

Hi vọng qua bài viết bạn có thể bắt đầu làm quen với GraphQL cũng như thấy những tính này mà nó mang lại.

References

https://medium.com/skyshidigital/easy-build-api-using-laravel-and-graphql-mutation-part-2-14dfc9ceb44c
https://medium.com/skyshidigital/easy-build-api-using-laravel-and-graphql-jwt-authentication-part-3-ea15c2421408