Xây dựng GraphQL server với Node.js
Bài đăng này đã không được cập nhật trong 6 năm
Mô tả
Ở bài này mình sẽ xây dựng GraphQL server với Node.js Sử dụng các công nghệ chính:
- Express, apollo server express.
- Database: Postgresql với Knex query.
- Mội vài thư viên Javascript khác : dataloader để giải quyết N+1, babel-node để sử dụng ES6 và nodemon để cập nhật server khi thay đổi code.
- Docker for setup development.
Cấu trúc thư mục
Bao gồm các thư mục chính:
- bussiness: thực ra bạn có thể sử dụng Bookself ORM. Định nghĩa các đối tượng và các hàm để giao tiếp với database
- knex.file.js, db (index.js, /migrations, /queryBuilders, /seeds): Dùng cho Knex.js để tương tác với database Postgresql
- Dockerfile, docker-images/db/Dockerfile và docker-compose.yml: Các Dockerfile để xây dựng các images(các image tạo ra container để chạy service) và docker-compose để kết hợp các container với nhau thành 1 hệ thống.
- config.env: Cài đặt các giá trị để cấu hình hệ thống. Rất quan trọng khi sử dụng trong các môi trường khác nhau (development, production)
- presentation/schema: Định nghĩa schema (Type, Query, Mutation, resolvers) cho GraphQL
.
├── bussiness
│ └── hero.js
├── config.env
├── db
│ ├── index.js
│ ├── migrations
│ ├── queryBuilders
│ └── seeds
├── docker-compose.yml
├── Dockerfile
├── docker_images
│ └── db
├── index.js
├── knexfile.js
├── node_modules
├── package.json
├── presentation
│ └── schema.js
Cài đặt môi trường phát triển với Docker
Mình sẽ cần Docker image cho Node.js
FROM node:9.8-alpine
WORKDIR /usr/src/graph
EXPOSE 3000
CMD ["yarn", "run", "serve"]
Và 1 image nữa cho database Postgresql
FROM postgres:9.6.3
Sau đó kết hợp với docker-compose.yml:
- Server Node với tên là graph sẽ chạy ở cổng 3000
- Database Postgresql sẽ dùng cổng 5432
- Dùng file config.env để config các giá trị cần thiết cho Postgresql:
POSTGRES_USER=heroesuser
POSTGRES_PASSWORD=heroespassword
POSTGRES_DB=heroesdb
PGDATA=/data
DB_HOST=db
Và đây là file docker-compose.yml
# docker-compose.yml
version: '3'
services:
graph:
build:
context: .
image: heroes-graph
env_file: config.env
volumes:
- .:/usr/src/graph
ports:
- 3000:3000
links:
- db:db
db:
build:
context: ./docker_images/db
env_file: config.env
image: heroes-db
ports:
- 5432:5432
Server Node với Express
Chúng ta đã cài đặt xong môi trường phát triển với Docker.
Để hệ thống này bạn chỉ cần chạy
docker-compose build
và docker-compose up
là xong.
Như đã nói ở trên mình dùng 1 vài thư viện js: express, apollo-server-express, graphql, graphql-tools, knex, pg body-parser, dataloader
và babel để build ES6 và nodemon để cập nhật code mỗi khi thay đổi.
yarn add express graphql, graphql-tools, apollo-server-express, dataloader, knex, pg
yarn add -D nodemon babel-cli babel-plugin-transform-class-properties babel-preset-es2015 babel-preset-flow
Sau khi cài đặt thì chúng ta sẽ được file package.json như này:
{
"devDependencies": {
"babel-cli": "^6.26.0",
"babel-plugin-transform-class-properties": "^6.24.1",
"babel-preset-es2015": "^6.24.1",
"babel-preset-flow": "^6.23.0",
"nodemon": "^1.17.2"
},
"scripts": {
"serve": "nodemon index.js --exec babel-node"
},
"dependencies": {
"apollo-server-express": "^1.3.2",
"body-parser": "^1.18.2",
"dataloader": "^1.4.0",
"express": "^4.16.3",
"graphql": "^0.13.1",
"graphql-tools": "^2.23.0",
"knex": "^0.14.4",
"pg": "^7.4.1"
}
Server Express của chúng ta sẽ như này
Mình dùng Express chạy server runtime và thêm vào đó là cài đặt graphqlExpress và graphiqlExpress.
// index.js
import express from 'express'
import bodyParser from 'body-parser'
import { graphqlExpress, graphiqlExpress } from 'apollo-server-express'
import schema from './presentation/schema'
import Hero from './bussiness/hero'
const PORT = 3000
const app = express()
app.use('/graphql', bodyParser.json(),graphqlExpress({
schema,
context: {
dataLoaders: {
hero: Hero.getLoaders(),
}
},
debug: true
}))
app.get('/graphiql', graphiqlExpress({ endpointURL: '/graphql' }))
app.listen(PORT)
Định nghĩa schema cho GraphQL
Mình định nghĩa
- Type: Hero
- Query: heroes (lấy danh sách các hero), hero(lấy về hero với id)
Các bạn có thể tham khảo về GrapQl
import { makeExecutableSchema } from 'graphql-tools'
import Hero from '../bussiness/hero'
const typeDefs = [`
type Hero {
id: Int!
firstName: String
lastName: String
heroName: String
enemy: Hero
}
type Query {
heroes: [Hero]
hero(id: Int!): Hero
}
schema {
query: Query
}
`]
const resolvers = {
Query: {
heroes: async (_, args, ctx) => Hero.loadAll(ctx, args),
hero: async (_, args, ctx) => Hero.load(ctx, args),
},
Hero: {
enemy: async (hero, args, ctx) => Hero.load(ctx, { id: hero.enemyId })
}
}
const schema = makeExecutableSchema({ typeDefs, resolvers })
export default schema
Cấu hình database với Knex.js
// knexfile.js
module.exports = {
development: {
client: 'pg',
connection: {
host: 'localhost',
port: 5432,
user: 'heroesuser',
password: 'heroespassword',
database: 'heroesdb',
},
migrations: {
directory: './db/migrations',
},
seeds: {
directory: './db/seeds',
},
},
};
Tạo các bảng dữ liệu với Knex migrations
- Table Hero
// migrations
exports.up = function(knex, Promise) {
return knex.schema.createTableIfNotExists('Heroes', function(table) {
table.increments('id')
table.string('firstName')
table.string('lastName')
table.string('heroName')
})
};
exports.down = function(knex, Promise) {
return knex.schema.dropTableIfExists('Heroes')
};
- Thêm column enemyId như là foreign key
// migrations
exports.up = function(knex, Promise) {
return knex.schema.table('Heroes', function(table) {
table.integer('enemyId').references('id').inTable('Heroes')
})
};
exports.down = function(knex, Promise) {
return knex.schema.table('Heroes', function() {
table.dropColumn('enemyId')
})
};
Các queries đến database với Knex
// queryBuilders
import db from '..'
class Hero {
static async getById(id: number) {
return db
.first()
.table('Heroes')
.where('id', id)
}
static async getByIds(ids: Array<number>) {
return db
.select()
.table('Heroes')
.whereIn('id', ids)
}
static async getAll() {
return db
.select()
.table('Heroes')
}
}
export default Hero
Tóm lược
Vậy là mình đã xây dựng xong GraphQl server với Nodejs
Tham khảo từ:
Các bạn có thể xem code của mình tại: https://gitlab.com/thinhnv.58/graphql_formation
Chúc bạn thành công !
All rights reserved