Sử dụng GraphQL với Laravel và Vue
Bài đăng này đã không được cập nhật trong 6 năm
Đây là ví dụ nho nhỏ về GraphQL sử dụng trong thực tế, có khá nhiều bài viết đã sử dụng với React, bài này mình sẽ dùng với Vue làm client truy vấn GraphQL và Laravel sẽ chịu trách nhiệm làm server GraphQL trả về dữ liệu cho client.
1. Xây dựng server
1.1 Cài đặt Laravel và xây dựng Database + Model
- Trước tiên bạn khởi taọ một project Laravel mới và chạy ngon lành đi.
- Bài này mình chỉ làm đơn giản, mình sẽ tạo 2 bảng là
user
vàprofile
, user thì ta có sẵn migration rồi, giờ mình sẽ Model và migration cho Profile, ta chạy lệnhphp artisan make:model Profile -m
. Đây là bảng User mình sẽ dùng, và sẽ rất ngắn gọn.
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->string('email', 100)->unique();
$table->string('password');
$table->timestamps();
});
Còn đây là bảng profile, cũng tương tự rất ngắn gọn.
Schema::create('profiles', function (Blueprint $table) {
$table->increments('id');
$table->integer('user_id');
$table->string('first_name')->nullable();
$table->string('last_name')->nullable();
$table->string('address')->nullable();
$table->timestamps();
});
1.2 Cấu hình GraphQL cho server
Đã xong bước tạo Model và database, giờ mình sẽ bắt đầu cài đặt và cấu hình GraphQL. Bây giờ chúng ta cùng nhau từng bước tạo một server GraphQL.
Trước tiên bạn cần chạy composer require folklore/graphql
để cài package GraphQL cho ứng dụng.
Sau đó publish các file cấu hình và view của package trên bằng lệnh sau php artisan vendor:publish --provider="Folklore\GraphQL\ServiceProvider"
. Bạn sẽ thấy 1 file mới config/graphql.php
, đây sẽ chứa các thông số cấu hình cho GraphQL.
Định nghĩa các Type
Các Type trong GraphQL là khai báo kiểu dữ liệu sẽ được trả về, và bạn cũng có thể tùy biến dữ liệu trả về từ dữ liệu gốc sao cho hợp lý.
Mình sẽ định nghĩa App\GraphQL\Type\UserType
như sau:
<?php
namespace App\GraphQL\Type;
use GraphQL;
use GraphQL\Type\Definition\Type;
use Folklore\GraphQL\Support\Type as GraphQLType;
class UserType extends GraphQLType
{
public function fields()
{
return [
'id' => [
'type' => Type::nonNull(Type::int()),
'description' => 'The id of the user',
],
'email' => [
'type' => Type::string(),
'description' => 'The email of user'
],
'created_at' => [
'type' => Type::string(),
'description' => 'Creation datetime'
],
'updated_at' => [
'type' => Type::string(),
'description' => 'Updating datetime'
],
'profile' => [
'type' => GraphQL::type('Profile'),
],
];
}
protected function resolveCreatedAtField($root, $args)
{
return $root->created_at->format('Y-m-d H:i:s');
}
protected function resolveUpdatedAtField($root, $args)
{
return $root->updated_at->format('Y-m-d H:i:s');
}
protected function resolveProfileField($root, $args)
{
return $root->profile;
}
}
Thực ra bảng profile này trong thực tế sẽ nằm luôn trong bảng user, tuy nhiên mình muốn tách ra để sử dụng truy vấn quan hệ trong GraphQL, mình sẽ khai báo App\GraphQL\Type\ProfileType
như sau:
<?php
namespace App\GraphQL\Type;
use GraphQL\Type\Definition\Type;
use Folklore\GraphQL\Support\Type as GraphQLType;
class ProfileType extends GraphQLType
{
public function fields()
{
return [
'id' => [
'type' => Type::nonNull(Type::int()),
'description' => 'The id of the user',
],
'first_name' => [
'type' => Type::string(),
],
'last_name' => [
'type' => Type::string(),
],
'address' => [
'type' => Type::string(),
],
'created_at' => [
'type' => Type::string(),
'description' => 'Creation datetime'
],
'updated_at' => [
'type' => Type::string(),
'description' => 'Updating datetime'
],
];
}
protected function resolveCreatedAtField($root, $args)
{
return $root->created_at->format('Y-m-d H:i:s');
}
protected function resolveUpdatedAtField($root, $args)
{
return $root->updated_at->format('Y-m-d H:i:s');
}
}
Như thế là mình đã xay dựng xong các Type mình sẽ dùng trong bài này. Sau khi xây dựng xong các Type trên thì chúng ta vào khai báo trong config: config/graphql.php
, bạn tìm đến dòng type
để thêm vào như sau:
'types' => [
'User' => App\GraphQL\Type\UserType::class,
'Profile' => App\GraphQL\Type\ProfileType::class,
],
Định nghĩa các Query
Giờ mình sẽ định nghĩa các query, đây là nơi bạn sẽ xử lý cách trả về dữ liệu cho Client sao cho hợp lý. Mình sẽ có file App\GraphQL\Querry\UserQuery
như sau:
<?php
namespace App\GraphQL\Query;
use GraphQL;
use App\User;
use GraphQL\Type\Definition\Type;
use Folklore\GraphQL\Support\Query;
class UserQuery extends Query
{
protected $attributes = [
'name' => 'users'
];
public function type()
{
return Type::listOf(GraphQL::type('User'));
}
public function args()
{
return [
'id' => ['name' => 'id', 'type' => Type::int()],
'email' => ['name' => 'email', 'type' => Type::string()],
'first' => ['name' => 'first', 'type' => Type::int()],
];
}
public function resolve($root, $args)
{
$users = new User();
if (isset($args['first'])) {
$users = $users->take($args['first'])->orderBy('id', 'desc');
}
if (isset($args['id'])) {
$users = $users->where('id', $args['id']);
}
if (isset($args['email'])) {
$users = $users->where('email', 'like', "%{$args['email']}%");
}
return $users->get();
}
}
Và bạn cũng cần khai báo vào file app/graphql.php
tại schemas
dòng sau:
'schemas' => [
'default' => [
'query' => [
'users' => \App\GraphQL\Query\UserQuery::class,
],
'mutation' => [
]
]
],
Thế là giờ bạn có thể test được query rồi đấy, để test query ở trên bạn có thể vào url graphiql
để test.
Ở bài này mình sẽ ko tạo Mutation, các bạn hãy tự tìm hiểu thêm nhé.
2. Client
Như tiêu đề bài viết, mình sẽ dùng Vue để xây dựng client và gọi dữ liệu ở Server vừa xây dựng ở trên. Ngoài các package có sẵn khi cài Laravel, bạn thêm cho mình các package sau:
"dependencies": {
"apollo-cache-inmemory": "^1.1.4",
"apollo-client": "^2.0.4",
"apollo-link": "^1.0.7",
"apollo-link-context": "^1.0.3",
"apollo-link-http": "^1.3.2",
"graphql": "^0.12.3",
"graphql-tag": "^2.6.1",
"vue-apollo": "^3.0.0-alpha.3"
}
Mình sẽ dùng Vue để xây dựng một client nho nhỏ, như ở đây mình chỉ hiển thị User list thôi, cơ bản là cách cấu hình của GraphQL với Vue thế nào là được rồi. Trước tiên là file main chứa các config cho Vue.
import Vue from 'vue'
import { ApolloClient } from 'apollo-client'
import { HttpLink } from 'apollo-link-http'
import { setContext } from 'apollo-link-context'
import { InMemoryCache } from 'apollo-cache-inmemory'
import VueApollo from 'vue-apollo'
import App from './App'
const httpLink = new HttpLink({
// You should use an absolute URL here
uri: 'http://localhost:8080/graphql'
})
const authLink = setContext((_, { headers }) => {
return {
...headers,
// authorization: token //for authentication
}
})
// Create the apollo client
const apolloClient = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache(),
connectToDevTools: true,
})
// Install the vue plugin
Vue.use(VueApollo)
const apolloProvider = new VueApollo({
defaultClient: apolloClient
})
/* eslint-disable no-new */
new Vue({
el: '#root',
provide: apolloProvider.provide(),
template: '<App/>',
components: { App }
})
Giờ là Component Root cho ứng dụng, mình sẽ tạo như sau. Mình dùng Bootstrap cho tiện nhá các bạn
<template>
<div class="app">
<UserList />
</div>
</template>
<script>
import UserList from './components/UserList'
export default {
name: 'app',
components: {
'UserList': UserList,
},
}
</script>
<style lang="scss">
@import '~bootstrap/dist/css/bootstrap.css';
</style>
Mình tạo 1 file chứa các câu query của GraphQL, mình tách ra như các bạn tách các api khi dùng RESTful.
import gql from 'graphql-tag'
export const ALL_USERS_QUERY = gql`
query Users {
users {
id
email
created_at
profile {
first_name
last_name
address
}
}
}
`
Và cuối cùng là file hiển thị list user, trong đó có cả query để fetch dữ liệu
<template>
<section class="section">
<div class="container">
<div class="columns">
<div class="column">
<h2 class="title">Users</h2>
<table class="table is-striped is-narrow is-hoverable is-fullwidth">
<thead>
<tr>
<th>Email</th>
<th>Address</th>
<th>Full Name</th>
<th>Join At</th>
</tr>
</thead>
<tbody>
<tr v-for="user in users" :key="user.id">
<td>{{ user.email }}</td>
<td>{{ user.profile.address }}</td>
<td>{{ `${user.profile.first_name} ${user.profile.last_name}` }}</td>
<td>{{ user.created_at }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</section>
</template>
<script>
import { ALL_USERS_QUERY } from '../graphql'
// Component def
export default {
name: 'UserList',
// Local state
data() {
return {
users: [],
}
},
// Apollo GraphQL
apollo: {
users: {
query: ALL_USERS_QUERY
},
},
}
</script>
Như thế là mình đã làm xong một ứng dụng nho nhỏ ứng dụng GraphQL cho Vue. Các bạn có thể tham khảo source code mình viết tại đây https://github.com/dinhvantai/graphql-laravel-vue
Bài viết mình có tham khảo tại: http://www.qcode.in/build-api-for-twitter-like-app-using-graphql-in-laravel/
All rights reserved