Grapql cách viết api. Cách viết do Facebook lập ra, để gom mọi api về 1 api. Api này có khả năng:
Cá nhân mình thấy thư viện này dễ hiểu, dễ cấu hình. Tuy nhiên, việc viết code định graphql viết theo một chuẩn mới khá khó viết so với cấu trúc mặc định của graphql.
Tham khảo: https://www.twilio.com/blog/build-graphql-powered-api-laravel-php
Lưu ý: Khi thực hiện code link trên nhớ thay App\Book =>> App\Models\Book. Vì Laravel mới đã chuyển model và folder Models.
Mình thấy thư viện này dùng khá phổ biến. Do cấu trúc code giống cấu trúc của graphql.
Tham khảo: https://www.toptal.com/graphql/laravel-graphql-server-tutorial
Phần mềm tên là GraphiQL khá dễ sử dụng.
Link download: https://www.electronjs.org/apps/graphiql
composer require nuwave/lighthouse
php artisan vendor:publish --provider="Nuwave\Lighthouse\LighthouseServiceProvider"
Sau đó sẽ có file config/lighthouse.php để cấu hình cho light-house gồm route,…
'schema' => [ 'register' => base_path('graphql/schema.graphql'), // nơi đăng ký file định nghĩa graphql ],
Chạy test thử:
php artisan serve // Vào phần mềm graphiQL mục 3.1 để chạy http://localhost:8000/graphql
Schema có nhiều type. Có 3 loại mặc định:
Định nghĩa ở graphql/schema.graphql
type Query { hello: String! }
Tạo query file
php artisan lighthouse:query Hello
Sẽ sinh ra file tương ứng App\GraphQL\Queries. Function __invoke() sẽ xử lý và trả data về
<?php namespace App\GraphQL\Queries; class Hello { public function __invoke(): string { return 'world!'; } }
Như vậy, khi gọi
{ hello } // result { "data": { "hello": "world!" } }
type Query { greet(name: String!): String // greet(name: String = "you"): String => set param mặc định khi không truyển vào // param không bắt buộc }
<?php namespace App\GraphQL\Queries; class Greet { public function __invoke($rootValue, array $args): string { return "Hello, {$args['name']}!"; } }
// gọi api { greet(name: "Foo") } // result { "data": { "greet": "Hello, Foo!" } }
@all @paginate @eq @orderBy @first @create @update @upsert // update or create if not exist @delete // relationship @hasOne @hasMany @belongsTo @belongsToMany @morphOne @morphTo @morphMany
Eloquent
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; class User extends Model { public function scopeVerified(Builder $query): Builder { return $query->whereNotNull('email_verified_at'); } }
type Query { users: [User]! @all(scopes: ["verified"]) }
npm install vue-apollo@3.0.3 graphql@15.2.0 apollo-boost@0.4.9
Add apollo Client in app.js
import ApolloClient from 'apollo-boost' const apolloClient = new ApolloClient({ // You should use an absolute URL here uri: 'http://127.0.0.1:8000/graphql' })
Import VueApollo
import VueApollo from 'vue-apollo' Vue.use(VueApollo)
Add apolloProvider
const apolloProvider = new VueApollo({ defaultClient: apolloClient, })
Add apoloProvider to app Vue
const app = new Vue({ el: '#app', router, apolloProvider });
<script> import gql from 'graphql-tag' export default { apollo: { // Simple query that will update the 'hello' vue property posts: gql`{ posts { id title } }`, }, } </script>
Trong một query sẽ gồm 2 phần
<script> import gql from 'graphql-tag' export default { apollo: { post: { query: gql` query ($id: ID!) { post(id: $id) { id title content author { id name avatar } topic { name slug } } }`, variables() { return { id: this.$route.params.id } } } } } </script>
// check loading $apollo.loading // Check a query loading $apollo.queries.posts.loading //example <template> <div> List Post <div>Loading: {{ $apollo.loading }}</div> <div>Is posts loading: {{ $apollo.queries.posts.loading }}</div> </div> </template>
<script> import gql from 'graphql-tag' export default { apollo: { post: { query: gql` ...`, variables() { return { id: this.$route.params.id } }, error() { this.$router.push({ name: '404' }); } } } } </script>
import CardAdd from '../graphql/CardAdd.gql' this.$apollo.mutate({ mutation: CardAdd, variables: { title: 'Added through mutation', list_id: 1, order: 1 } }); // Có update addCard() { const self = this; this.$apollo.mutate({ mutation: CardAdd, variables: { title: this.title, list_id: this.list.id, order: 1 }, update(store, {data: {cardAdd} }) { const data = store.readQuery({ query: BoardQuery, variables: { id: Number(self.list.board_id)} }); data.board.lists.find(list => (list.id == self.list.id)).cards.push(cardAdd); store.writeQuery({ query: BoardQuery, data }); } }); this.closed(); },
Tạo file xxx.gql, lấy đoạn query sang file này
Đặt tên file vào sau query.
Ví dụ: tên file là: BoardWithListsAndCards.gql thì query BoardWithListsAndCards( …
query BoardWithListsAndCards($id: ID!) { board(id: $id) { title color lists { id title cards { id title } } } }
Import vào component
import BoardQuery from './graphql/BoardWithListsAndCards.gql' // dòng này export default { apollo: { board: { query: BoardQuery, // dòng này variables() { return { id: 1 } }, error() { this.$router.push({ name: '404' }); } } } }
Cấu hình lại webpack mix laravel
const mix = require('laravel-mix'); /*============================================ * Thêm đoạn này *============================================*/ mix.extend( 'graphql', new class { dependencies() { return ['graphql', 'graphql-tag'] } webpackRules() { return { test: /\.(graphql|gql)$/, exclude: /node_modules/, loader: 'graphql-tag/loader' } } }() ); /*============================================*/ mix.js("resources/js/app.js", "public/js") .postCss("resources/css/app.css", "public/css", [ require("tailwindcss"), ]); /*============================================ * và thêm đoạn này *============================================*/ mix.graphql(); /*============================================*/
Build lại
npm run watch
query ($id: ID!) { post(id: $id) { id title } }
Tên function relationship trong model đặt thế nào thì trong định nghĩa type của graphql cũng sử dụng tên đó định nghĩa, nếu không sẽ bị lỗi:
Call to undefined relationship [lists] on model
Tui mất khá nhiều thời gian vì lỗi này.
Repository github: https://github.com/tronghao/learn-graphql-blogql
Cài laravel và các dependences
composer create-project laravel/laravel=7.12.0 blog-ql --prefer-dist composer require nuwave/lighthouse=4.14.1 composer require mll-lab/laravel-graphql-playground=2.1.0 // Nếu laravel version >= 8 composer require --dev barryvdh/laravel-ide-helper // Nếu laravel version 7 composer require --dev barryvdh/laravel-ide-helper 2.8
composer require laravel/ui composer require laravel/ui:^2.4 // với laravel 7 php artisan ui vue
Cài nodejs
// check node -v npm -v
Run lệnh
npm install && npm run dev
Install tailwindcss
npm install tailwindcss@1.4.6 // delete folder resource/js/sass // create file resource/css/app.css @tailwind base; @tailwind components; @tailwind utilities npx tailwindcss init
Chỉnh webpack.mix.js
mix.js("resources/js/app.js", "public/js") .postCss("resources/css/app.css", "public/css", [ require("tailwindcss"), ]); // sau đó npm run dev
add version to mix
// thêm vào file webpack.mix.js if (mix.inProduction()) { mix.version(); }
Publish vendor light-housr
php artisan vendor:publish --provider="Nuwave\Lighthouse\LighthouseServiceProvider" --tag=schema
Create migration and model Post and Topic
Add relationship model
install vue-router
Route::get('/{any?}', function () { return view('welcome'); })->where('any', '^(?!graphql)[\/\w\.-]*');
install vue apollo
install graphql-tag
npm i graphql-tag@2.10.3
intall extension Tailwind css IntelliSense in vs code
install moment.js
npm i moment@2.27.0
Tạo filter vuejs
Vue.filter("timeago", value => moment(value).fromNow() ); Vue.filter("longDate", value => moment(value).format("MMMM Do YYYY") );
Xử lý 404
const routes = [ { path: '/', name: 'index', component: PostList }, { path: '/topics/:slug', name: 'topic', component: TopicPostList, }, { path: '/post/:id', name: 'post', component: Post }, { path: '/authors/:id', name: 'author', component: AuthorPostList }, { path: '*', name: '404', component: { template: '<div>Not Found</div>' } } ];
<script> import gql from 'graphql-tag' export default { apollo: { post: { query: gql` ...`, variables() { return { id: this.$route.params.id } }, error() { this.$router.push({ name: '404' }); } } } } </script>
composer create-project laravel/laravel=7.12.0 laravello --prefer-dist composer require nuwave/lighthouse=4.15.0 composer require mll-lab/laravel-graphql-playground=2.3.0 composer require laravel/telescope=3.5.0 composer require --dev barryvdh/laravel-ide-helper 2.8 composer require laravel/ui:^2.4
npm i vue-apollo@3.0.3 graphql@15.2.0 apollo-boost@0.4.9
Mutation
type Mutation { cardAdd(title: String!, order: Int, list_id: ID!, owner_id: ID!): Card! @create } // query mutation { cardAdd(title: "xxx", order: 1, list_id:1, owner_id: 1) { id title } }
Sử dụng input để gọn hơn.
type Mutation { cardAdd(input: CardAddInput! @spread): Card! @create } input CardAddInput { title: String! order: Int list_id: ID! owner_id: ID! } // cách query mutation CardAdd($list_id: ID!, $title: String!, $order: Int!) { cardAdd(input: {title: $title, order: $order, list_id:$list_id, owner_id: 1}) { id title } }