12/08/2018, 16:29

Giới thiệu graphql và cách tương tác graphql với koajs

Hôm nay mình xin giới thiệu 1 chút về graphql và cách tương tác với một server nodejs dùng koajs . Giới thiệu GraphQL là một ngôn ngữ query cho API dùng để viết các câu API một cách uyển chuyển, chính xác những gì cần có. Các khái niệm trong graphql Fields: Là một kiểu xác định các thuộc ...

Hôm nay mình xin giới thiệu 1 chút về graphql và cách tương tác với một server nodejs dùng koajs.

Giới thiệu

GraphQL là một ngôn ngữ query cho API dùng để viết các câu API một cách uyển chuyển, chính xác những gì cần có.

Các khái niệm trong graphql

  1. Fields: Là một kiểu xác định các thuộc tính cho object. Ví dụ ở đây ta có field name.
{
  hero {
    name
  }
}
  1. Arguments Đơn giản là param cần push lên server để get chính xác record mong muốn theo các điều kiện. Ví dụ, ở đây ta có biến id:
{
  human(id: "1000") {
    name
    height(unit: FOOT)
  }
}
  1. Aliases Là cách đặt tên khác cho kết quả trả về:
{
  empireHero: hero(episode: EMPIRE) {
    name
  }
  jediHero: hero(episode: JEDI) {
    name
  }
}
Kết quả là:
{
  "data": {
    "empireHero": {
      "name": "Luke Skywalker"
    },
    "jediHero": {
      "name": "R2-D2"
    }
  }
}
  1. Variables Giống như parameter, là một định dạng dynamic cho biến:
query HeroNameAndFriends($episode: Episode) {
  hero(episode: $episode) {
    name
    friends {
      name
    }
  }
}
Với biến:
{
  "episode": "JEDI"
}
Sẽ ra kết quả:
{
  "data": {
    "hero": {
      "name": "R2-D2",
      "friends": [
        {
          "name": "Luke Skywalker"
        },
        {
          "name": "Han Solo"
        },
        {
          "name": "Leia Organa"
        }
      ]
    }
  }
}

Và dĩ nhiên chúng ta cũng có thể định dạng loại biến, như là loại Episode ở đây:

query HeroNameAndFriends($episode: Episode = "JEDI") {
  hero(episode: $episode) {
    name
    friends {
      name
    }
  }
}
  1. Directives Là cách chúng ta dùng biến để change cấu trúc của query một cách tự động: Ví dụ:
query Hero($episode: Episode, $withFriends: Boolean!) {
  hero(episode: $episode) {
    name
    friends @include(if: $withFriends) {
      name
    }
  }
}
Với biến:
{
  "episode": "JEDI",
  "withFriends": false
}
Chúng ta sẽ quyết định kết quả trả về có include thêm friends không:
{
  "data": {
    "hero": {
      "name": "R2-D2"
    }
  }
}
  1. Mutations Là cách thay đổi data trên server, dùng như việc create và update trong REST.
mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {
  createReview(episode: $ep, review: $review) {
    stars
    commentary
  }
}

Với biến:

{
  "ep": "JEDI",
  "review": {
    "stars": 5,
    "commentary": "This is a great movie!"
  }
}

Ta sẽ có kết quả:
{
  "data": {
    "createReview": {
      "stars": 5,
      "commentary": "This is a great movie!"
    }
  }
}

Tương tác graphql với koajs

Hiện tại trong Koajs có một package rất phù hợp để cấu hình server graphql là koa-graphql.

  • Lắp đặt: npm install --save koa-graphql
  • Tương tác trực tiếp với Koa thông qua koa-router:
const Koa = require('koa');
const Router = require('koa-router'); // koa-router@7.x
const graphqlHTTP = require('koa-graphql');

const app = new Koa();
const router = new Router();

router.all('/graphql', graphqlHTTP({
  schema: MyGraphQLSchema,
  graphiql: true
}));

app.use(router.routes()).use(router.allowedMethods());

Cài đặt một chức năng đơn giản là load project về client với query dựa trên graphql:

  1. Database:
  • Bảng projects gồm: id | name | description | logo | num_star | owner_id | is_pinned
  • Bảng users gồm: id | fullname | username | email | avatar.
  1. Define type object tương tác là Project hay User:
import {
  GraphQLBoolean,
  GraphQLEnumType,
  GraphQLFloat,
  GraphQLInt,
  GraphQLList,
  GraphQLObjectType,
  GraphQLString,
  GraphQLInterfaceType
} from 'graphql';

export const UserType = new GraphQLObjectType({
  name: 'User',
  description: 'This represents a User',
  fields: () => {
    return {
      id: {
        type: GraphQLInt,
        resolve(user) {
          return user.id;
        }
      },
      fullname: {
        type: GraphQLString,
        resolve(user) {
          return user.fullname;
        }
      },
      username: {
        type: GraphQLString,
        resolve(user) {
          return user.username;
        }
      },
      email: {
        type: GraphQLString,
        resolve(user) {
          return user.email;
        }
      },
      avatar: {
        type: GraphQLString,
        resolve(user) {
          return user.avatar;
        }
      },
    }
  }
});

export const ProjectType = new GraphQLObjectType({
  name: 'Project',
  description: 'This represents a Project',
  fields: () => {
    return {
      id: {
        type: GraphQLInt,
        resolve(project) {
          return project.id;
        }
      },
      name: {
        type: GraphQLString,
        resolve(project) {
          return project.name;
        }
      },
      description: {
        type: GraphQLString,
        resolve(project) {
          return project.description;
        }
      },
      logo: {
        type: GraphQLString,
        resolve(project) {
          return project.logo;
        }
      },
      num_star: {
        type: GraphQLInt,
        resolve(project) {
          return project.num_star;
        }
      },
      user: {
        type: UserType,
        resolve(project) {
          return project.getUser();
        }
      },
    }
  }
});
  1. Viết query get list project có tương tác phân trang:
import {
  GraphQLBoolean,
  GraphQLEnumType,
  GraphQLFloat,
  GraphQLInt,
  GraphQLList,
  GraphQLObjectType,
  GraphQLString,
  GraphQLNonNull,
  GraphQLInterfaceType
} from 'graphql';

import models from '../../models';

import {
  ProjectType
} from './types';

const queries = {
  projectsHomePage: {
    type: new GraphQLList(ProjectType),
    args: {
      offset: {
        type: new GraphQLNonNull(GraphQLInt)
      },
      limit: {
        type: new GraphQLNonNull(GraphQLInt)
      }
    },
    resolve(_, args) {
      return models.Project.findAll({
        offset: args.offset,
        limit: args.limit,
      })
    }
  },
};

export default queries;

  1. Ta dùng query để lấy kết quả, nhưng sẽ dùng mutation để thay đổi update data:
  • Để làm được vậy, ta define loại input cần đưa vào:
import {
  GraphQLInt,
  GraphQLFloat,
  GraphQLList,
  GraphQLInputObjectType,
  GraphQLNonNull,
  GraphQLString,
  GraphQLScalarType,
  GraphQLError
} from 'graphql';

export const ProjectInputType = new GraphQLInputObjectType({
  name: 'ProjectInputType',
  description: 'Project type',
  fields: () => ({
    name: { type: new GraphQLNonNull(GraphQLString)},
    description: { type: new GraphQLNonNull(GraphQLString)},
    logo: { type: new GraphQLNonNull(GraphQLString)},
    num_star: { type: new GraphQLNonNull(GraphQLInt)},
    owner_id: { type: new GraphQLNonNull(GraphQLInt)},
    users_id: { type: new GraphQLList(GraphQLString)},
  })
});

  • Đây là ví dụ dùng mutation để tạo thêm project (ở đây để tương tác database tôi dùng sequelize, các bạn không cần để ý kĩ code create đâu ạ             </div>
            
            <div class=
0