GraphQL 搭配 Koa 最佳入門實踐

naice發表於2018-01-08

GraphQL一種用為你 API 而生的查詢語言,2018已經到來,PWA還沒有大量投入生產應用之中就已經火起來了,GraphQL的應用或許也不會太遠了。前端的發展的最大一個特點就是變化快,有時候應對各種需求場景的變化,不得不去對介面開發很多版本或者修改。各種業務依賴強大的基礎資料平臺快速生長,如何高效地為各種業務提供資料支援,是所有人關心的問題。而且現在前端的解決方案是將檢視元件化,各個業務線既可以是元件的使用者,也可以是元件的生產者,如果能夠將其中通用的內容抽取出來提供給各個業務方反覆使用,必然能夠節省寶貴的開發時間和開發人力。那麼問題來了,前端通過元件實現了跨業務的複用,後端介面如何相應地提高開發效率呢?GraphQL,就是應對複雜場景的一種新思路。

官方解釋:

GraphQL 既是一種用於 API 的查詢語言也是一個滿足你資料查詢的執行時。 GraphQL 對你的 API 中的資料提供了一套易於理解的完整描述,使得客戶端能夠準確地獲得它需要的資料,而且沒有任何冗餘,也讓 API 更容易地隨著時間推移而演進,還能用於構建強大的開發者工具。

下面介紹一下GraphQL的有哪些好處:

  • 請求你所要的資料不多不少

  • 獲取多個資源只用一個請求

  • 自定義介面資料的欄位

  • 強大的開發者工具

  • API 演進無需劃分版本

本篇文章中將搭配koa實現一個GraphQL查詢的例子,逐步從簡單kao服務到mongodb的資料插入查詢再到GraphQL的使用, 讓大家快速看到:

  • 搭建koa搭建一個後臺專案
  • 後臺路由簡單處理方式
  • 利用mongoose簡單操作mongodb
  • 掌握GraphQL的入門姿勢

專案如下圖所示

1、搭建GraphQL工具查詢介面。

GraphQL 搭配 Koa 最佳入門實踐

2、前端用jq傳送ajax的使用方式

GraphQL 搭配 Koa 最佳入門實踐

入門專案我們都已經是預覽過了,下面我們動手開發吧!!!

lets do it

首先建立一個專案資料夾,然後在這個專案資料夾新建一個server.js(node服務)、config資料夾mongodb資料夾router資料夾controllers資料夾以及public資料夾(這個主要放前端靜態資料展示頁面),好啦,專案的結構我們都已經建立好,下面在server.js資料夾裡寫上

server.js

// 引入模組
import Koa from 'koa'
import KoaStatic from 'koa-static'
import Router from 'koa-router'
import bodyParser from 'koa-bodyparser'


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

// 使用 bodyParser 和 KoaStatic 中介軟體
app.use(bodyParser());
app.use(KoaStatic(__dirname + '/public'));

// 路由設定test
router.get('/test', (ctx, next) => {
  ctx.body="test page"
});

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

app.listen(4000);

console.log('graphQL server listen port: ' + 4000)
複製程式碼

在命令列npm install koa koa-static koa-router koa-bodyparser --save

安裝好上面幾個模組,

然後執行node server.js,不出什麼意外的話,你會發現報如下圖的一個error

GraphQL 搭配 Koa 最佳入門實踐

原因是現在的node版本並沒有支援es6的模組引入方式。

放心 我們用神器babel-polyfill轉譯一下就闊以了。詳細的請看阮一峰老師的這篇文章

下面在專案資料夾新建一個start.js,然後在裡面寫上以下程式碼:

start.js

require('babel-core/register')({
  'presets': [
    'stage-3',
    ["latest-node", { "target": "current" }]
  ]
})

require('babel-polyfill')
require('./server')

複製程式碼

然後 在命令列,執行npm install babel-core babel-polyfill babel-preset-latest-node babel-preset-stage-3 --save-dev安裝幾個開發模組。

安裝完畢之後,在命令列執行 node start.js,之後你的node服務安靜的執行起來了。用koa-router中介軟體做我們專案路由模組的管理,後面會寫到router資料夾中統一管理。

開啟瀏覽器,輸入localhost:4000/test,你就會發現訪問這個路由node服務會返回test page文字。如下圖

GraphQL 搭配 Koa 最佳入門實踐

yeah~~kao伺服器基本搭建好之後,下面就是,連結mongodb然後把資料儲存到mongodb資料庫裡面啦。

實現mongodb的基本資料模型

tip:這裡我們需要mongodb儲存資料以及利用mongoose模組操作mongodb資料庫

  • mongodb資料夾新建一個index.jsschema資料夾, 在 schema資料夾資料夾下面新建info.jsstudent.js

  • config資料夾下面建立一個index.js,這個檔案主要是放一下配置程式碼。

又一波檔案建立好之後,先在config/index.js下寫上鍊接資料庫配置的程式碼。

config/index.js

export default {
  dbPath: 'mongodb://localhost/graphql'
}

複製程式碼

然後在mongodb/index.js下寫上鍊接資料庫的程式碼。

mongodb/index.js

// 引入mongoose模組
import mongoose from 'mongoose'
import config from '../config'

// 同步引入 info model和 studen model
require('./schema/info')
require('./schema/student')

// 連結mongodb
export const database = () => {
  mongoose.set('debug', true)

  mongoose.connect(config.dbPath)

  mongoose.connection.on('disconnected', () => {
    mongoose.connect(config.dbPath)
  })
  mongoose.connection.on('error', err => {
    console.error(err)
  })

  mongoose.connection.on('open', async () => {
    console.log('Connected to MongoDB ', config.dbPath)
  })
}

複製程式碼

上面我們我們程式碼還載入了info.jsstuden.js這兩個分別是學生的附加資訊和基本資訊的資料模型,為什麼會分成兩個資訊表?原因是順便給大家介紹一下聯表查詢的基本方法(嘿嘿~~~)

下面我們分別完成這兩個資料模型

mongodb/schema/info.js

// 引入mongoose
import mongoose from 'mongoose'

// 
const Schema = mongoose.Schema

// 例項InfoSchema
const InfoSchema = new Schema({
  hobby: [String],
  height: String,
  weight: Number,
  meta: {
    createdAt: {
      type: Date,
      default: Date.now()
    },
    updatedAt: {
      type: Date,
      default: Date.now()
    }
  }
})
// 在儲存資料之前跟新日期
InfoSchema.pre('save', function (next) {
  if (this.isNew) {
    this.meta.createdAt = this.meta.updatedAt = Date.now()
  } else {
    this.meta.updatedAt = Date.now()
  }

  next()
})
// 建立Info資料模型
mongoose.model('Info', InfoSchema)
複製程式碼

上面的程式碼就是利用mongoose實現了學生的附加資訊的資料模型,用同樣的方法我們實現了student資料模型

mongodb/schema/student.js

import mongoose from 'mongoose'

const Schema = mongoose.Schema
const ObjectId = Schema.Types.ObjectId


const StudentSchema = new Schema({
  name: String,
  sex: String,
  age: Number,
  info: {
    type: ObjectId,
    ref: 'Info'
  },
  meta: {
    createdAt: {
      type: Date,
      default: Date.now()
    },
    updatedAt: {
      type: Date,
      default: Date.now()
    }
  }
})

StudentSchema.pre('save', function (next) {
  if (this.isNew) {
    this.meta.createdAt = this.meta.updatedAt = Date.now()
  } else {
    this.meta.updatedAt = Date.now()
  }

  next()
})

mongoose.model('Student', StudentSchema)
複製程式碼

實現儲存資料的控制器

資料模型都連結好之後,我們就新增一些儲存資料的方法,這些方法都寫在控制器裡面。然後在controler裡面新建info.jsstudent.js,這兩個檔案分別物件,操作info和student資料的控制器,分開寫為了方便模組化管理。

  • 實現info資料資訊的儲存,順便把查詢也先寫上去,程式碼很簡單

controlers/info.js

import mongoose from 'mongoose'
const Info = mongoose.model('Info')

// 儲存info資訊
export const saveInfo = async (ctx, next) => {
  // 獲取請求的資料
  const opts = ctx.request.body
  
  const info = new Info(opts)
  const saveInfo = await info.save() // 儲存資料
  console.log(saveInfo)
  // 簡單判斷一下 是否儲存成功,然後返回給前端
  if (saveInfo) {
    ctx.body = {
      success: true,
      info: saveInfo
    }
  } else {
    ctx.body = {
      success: false
    }
  }
}

// 獲取所有的info資料
export const fetchInfo = async (ctx, next) => {
  const infos = await Info.find({}) // 資料查詢

  if (infos.length) {
    ctx.body = {
      success: true,
      info: infos
    }
  } else {
    ctx.body = {
      success: false
    }
  }
}
複製程式碼

上面的程式碼,就是前端用post(路由下面一會在寫)請求過來的資料,然後儲存到mongodb資料庫,在返回給前端儲存成功與否的狀態。也簡單實現了一下,獲取全部附加資訊的的一個方法。下面我們用同樣的道理實現studen資料的儲存以及獲取。

  • 實現studen資料的儲存以及獲取

controllers/sdudent.js

import mongoose from 'mongoose'
const Student = mongoose.model('Student')

// 儲存學生資料的方法
export const saveStudent = async (ctx, next) => {
  // 獲取前端請求的資料
  const opts = ctx.request.body
  
  const student = new Student(opts)
  const saveStudent = await student.save() // 儲存資料

  if (saveStudent) {
    ctx.body = {
      success: true,
      student: saveStudent
    }
  } else {
    ctx.body = {
      success: false
    }
  }
}

// 查詢所有學生的資料
export const fetchStudent = async (ctx, next) => {
  const students = await Student.find({})

  if (students.length) {
    ctx.body = {
      success: true,
      student: students
    }
  } else {
    ctx.body = {
      success: false
    }
  }
}

// 查詢學生的資料以及附加資料
export const fetchStudentDetail = async (ctx, next) => {

  // 利用populate來查詢關聯info的資料
  const students = await Student.find({}).populate({
    path: 'info',
    select: 'hobby height weight'
  }).exec()

  if (students.length) {
    ctx.body = {
      success: true,
      student: students
    }
  } else {
    ctx.body = {
      success: false
    }
  }
}
複製程式碼

實現路由,給前端提供API介面

資料模型和控制器在上面我們都已經是完成了,下面就利用koa-router路由中介軟體,來實現請求的介面。我們回到server.js,在上面新增一些程式碼。如下

server.js

import Koa from 'koa'
import KoaStatic from 'koa-static'
import Router from 'koa-router'
import bodyParser from 'koa-bodyparser'

import {database} from './mongodb' // 引入mongodb
import {saveInfo, fetchInfo} from './controllers/info' // 引入info controller
import {saveStudent, fetchStudent, fetchStudentDetail} from './controllers/student' // 引入 student controller

database() // 連結資料庫並且初始化資料模型

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

app.use(bodyParser());
app.use(KoaStatic(__dirname + '/public'));

router.get('/test', (ctx, next) => {
  ctx.body="test page"
});

// 設定每一個路由對應的相對的控制器
router.post('/saveinfo', saveInfo)
router.get('/info', fetchInfo)

router.post('/savestudent', saveStudent)
router.get('/student', fetchStudent)
router.get('/studentDetail', fetchStudentDetail)

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

app.listen(4000);

console.log('graphQL server listen port: ' + 4000)
複製程式碼

上面的程式碼,就是做了,引入mongodb設定,info以及student控制器,然後連結資料庫,並且設定每一個設定每一個路由對應的我們定義的的控制器。

安裝一下mongoose模組 npm install mongoose --save

然後在命令列執行node start,我們伺服器執行之後,然後在給info和student新增一些資料。這裡是通過postman的谷歌瀏覽器外掛來請求的,如下圖所示

GraphQL 搭配 Koa 最佳入門實踐

yeah~~~儲存成功,繼續按照步驟多儲存幾條,然後按照介面查詢一下。如下圖

GraphQL 搭配 Koa 最佳入門實踐

嗯,如圖都已經查詢到我們儲存的全部資料,並且全部返回前端了。不錯不錯。下面繼續儲存學生資料。

tip: 學生資料儲存的時候關聯了資訊裡面的資料哦。所以把id寫上去了。

GraphQL 搭配 Koa 最佳入門實踐

同樣的一波操作,我們多儲存學生幾條資訊,然後查詢學生資訊,如下圖所示。

GraphQL 搭配 Koa 最佳入門實踐

好了 ,資料我們都已經儲存好了,鋪墊也做了一大把了,下面讓我們真正的進入,GrapgQL查詢的騷操作吧~~~~

重構路由,配置GraphQL查詢介面

別忘了,下面我們建立了一個router資料夾,這個資料夾就是統一管理我們路由的模組,分離了路由個應用服務的模組。在router資料夾新建一個index.js。並且改造一下server.js裡面的路由全部複製到router/index.js

順便在這個路由檔案中加入,graphql-server-koa模組,這是koa整合的graphql伺服器模組。graphql server是一個社群維護的開源graphql伺服器,可以與所有的node.js http伺服器框架一起工作:express,connect,hapi,koa和restify。可以點選連結檢視詳細知識點。

加入graphql-server-koa的路由檔案程式碼如下:

router/index.js


import { graphqlKoa, graphiqlKoa } from 'graphql-server-koa'
import {saveInfo, fetchInfo} from '../controllers/info'
import {saveStudent, fetchStudent, fetchStudentDetail} from '../controllers/student'


const router = require('koa-router')()

router.post('/saveinfo', saveInfo)
      .get('/info', fetchInfo)
      .post('/savestudent', saveStudent)
      .get('/student', fetchStudent)
      .get('/studentDetail', fetchStudentDetail)
      .get('/graphiql', async (ctx, next) => {
        await graphiqlKoa({endpointURL: '/graphql'})(ctx, next)
      })
module.exports = router

複製程式碼

之後把server.js的路由程式碼去掉之後的的程式碼如下:

server.js


import Koa from 'koa'
import KoaStatic from 'koa-static'
import Router from 'koa-router'
import bodyParser from 'koa-bodyparser'

import {database} from './mongodb'

database()

const GraphqlRouter = require('./router')

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

const port = 4000

app.use(bodyParser());
app.use(KoaStatic(__dirname + '/public'));

router.use('', GraphqlRouter.routes())

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

app.listen(port);

console.log('GraphQL-demo server listen port: ' + port)

複製程式碼

恩,分離之後簡潔,明瞭了很多。然後我們在重新啟動node服務。在瀏覽器位址列輸入http://localhost:4000/graphiql,就會得到下面這個介面。如圖:

GraphQL 搭配 Koa 最佳入門實踐

沒錯,什麼都沒有 就是GraphQL查詢服務的介面。下面我們把這個GraphQL查詢服務完善起來。

編寫GraphQL Schema

看一下我們第一張圖,我們需要什麼資料,在GraphQL查詢介面就編寫什麼欄位,就可以查詢到了,而後端需要定義好這些資料格式。這就需要我們定義好GraphQL Schema。

首先我們在根目錄新建一個graphql資料夾,這個資料夾用於存放管理graphql相關的js檔案。然後在graphql資料夾新建一個schema.js

這裡我們用到graphql模組,這個模組就是用javascript參考實現graphql查詢。向需要詳細學習,請使勁戳連結。

我們先寫好info的查詢方法。然後其他都差不多滴。

graphql/schema.js


// 引入GraphQL各種方法型別

import {
  graphql,
  GraphQLSchema,
  GraphQLObjectType,
  GraphQLString,
  GraphQLID,
  GraphQLList,
  GraphQLNonNull,
  isOutputType
} from 'graphql';

import mongoose from 'mongoose'
const Info = mongoose.model('Info') // 引入Info模組

// 定義日期時間 型別
const objType = new GraphQLObjectType({
  name: 'mete',
  fields: {
    createdAt: {
      type: GraphQLString
    },
    updatedAt: {
      type: GraphQLString
    }
  }
})

// 定義Info的資料型別
let InfoType = new GraphQLObjectType({
  name: 'Info',
  fields: {
    _id: {
      type: GraphQLID
    },
    height: {
      type: GraphQLString
    },
    weight: {
      type: GraphQLString
    },
    hobby: {
      type: new GraphQLList(GraphQLString)
    },
    meta: {
      type: objType
    }
  }
})

// 批量查詢
const infos = {
  type: new GraphQLList(InfoType),
  args: {},
  resolve (root, params, options) {
    return Info.find({}).exec() // 資料庫查詢
  }
}

// 根據id查詢單條info資料

const info = {
  type: InfoType,
  // 傳進來的引數
  args: {
    id: {
      name: 'id',
      type: new GraphQLNonNull(GraphQLID) // 引數不為空
    }
  },
  resolve (root, params, options) {
    return Info.findOne({_id: params.id}).exec() // 查詢單條資料
  }
}

// 匯出GraphQLSchema模組

export default new GraphQLSchema({
  query: new GraphQLObjectType({
    name: 'Queries',
    fields: {
      infos,
      info
    }
  })
})


複製程式碼

看程式碼的時候建議從下往上看~~~~,上面程式碼所說的就是,建立info和infos的GraphQLSchema,然後定義好資料格式,查詢到資料,或者根據引數查詢到單條資料,然後返回出去。

寫好了info schema之後 我們在配置一下路由,進入router/index.js裡面,加入下面幾行程式碼。

router/index.js


import { graphqlKoa, graphiqlKoa } from 'graphql-server-koa'
import {saveInfo, fetchInfo} from '../controllers/info'
import {saveStudent, fetchStudent, fetchStudentDetail} from '../controllers/student'

// 引入schema
import schema from '../graphql/schema'

const router = require('koa-router')()

router.post('/saveinfo', saveInfo)
      .get('/info', fetchInfo)
      .post('/savestudent', saveStudent)
      .get('/student', fetchStudent)
      .get('/studentDetail', fetchStudentDetail)




router.post('/graphql', async (ctx, next) => {
        await graphqlKoa({schema: schema})(ctx, next) // 使用schema
      })
      .get('/graphql', async (ctx, next) => {
        await graphqlKoa({schema: schema})(ctx, next) // 使用schema
      })
      .get('/graphiql', async (ctx, next) => {
        await graphiqlKoa({endpointURL: '/graphql'})(ctx, next) // 重定向到graphiql路由
      })
module.exports = router

複製程式碼

詳細請看註釋,然後被忘記安裝好npm install graphql-server-koa graphql --save這兩個模組。安裝完畢之後,重新執行伺服器的node start(你可以使用nodemon來啟動本地node服務,免得來回啟動。)

然後重新整理http://localhost:4000/graphiql,你會發現右邊會有查詢文件,在左邊寫上查詢方式,如下圖

GraphQL 搭配 Koa 最佳入門實踐

重整Graphql程式碼結構,完成所有資料查詢

現在是我們把schema和type都寫到一個檔案上面了去了,如果資料多了,欄位多了變得特別不好維護以及review,所以我們就把定義type的和schema分離開來,說做就做。

graphql資料夾新建info.jsstuden.js,檔案,先把info type 寫到info.js程式碼如下

graphql/info.js

import {
  graphql,
  GraphQLSchema,
  GraphQLObjectType,
  GraphQLString,
  GraphQLID,
  GraphQLList,
  GraphQLNonNull,
  isOutputType
} from 'graphql';

import mongoose from 'mongoose'
const Info = mongoose.model('Info')


const objType = new GraphQLObjectType({
  name: 'mete',
  fields: {
    createdAt: {
      type: GraphQLString
    },
    updatedAt: {
      type: GraphQLString
    }
  }
})

export let InfoType = new GraphQLObjectType({
  name: 'Info',
  fields: {
    _id: {
      type: GraphQLID
    },
    height: {
      type: GraphQLString
    },
    weight: {
      type: GraphQLString
    },
    hobby: {
      type: new GraphQLList(GraphQLString)
    },
    meta: {
      type: objType
    }
  }
})


export const infos = {
  type: new GraphQLList(InfoType),
  args: {},
  resolve (root, params, options) {
    return Info.find({}).exec()
  }
}


export const info = {
  type: InfoType,
  args: {
    id: {
      name: 'id',
      type: new GraphQLNonNull(GraphQLID)
    }
  },
  resolve (root, params, options) {
    return Info.findOne({
      _id: params.id
    }).exec()
  }
}

複製程式碼

分離好info type 之後,一鼓作氣,我們順便把studen type 也完成一下,程式碼如下,原理跟info type 都是相通的,

graphql/student.js


import {
  graphql,
  GraphQLSchema,
  GraphQLObjectType,
  GraphQLString,
  GraphQLID,
  GraphQLList,
  GraphQLNonNull,
  isOutputType,
  GraphQLInt
} from 'graphql';

import mongoose from 'mongoose'

import {InfoType} from './info'
const Student = mongoose.model('Student')


let StudentType = new GraphQLObjectType({
  name: 'Student',
  fields: {
    _id: {
      type: GraphQLID
    },
    name: {
      type: GraphQLString
    },
    sex: {
      type: GraphQLString
    },
    age: {
      type: GraphQLInt
    },
    info: {
      type: InfoType
    }
  }
})


export const student = {
  type: new GraphQLList(StudentType),
  args: {},
  resolve (root, params, options) {
    return Student.find({}).populate({
      path: 'info',
      select: 'hobby height weight'
    }).exec()
  }
}


複製程式碼

tips: 上面因為有了聯表查詢,所以引用了info.js

然後調整一下schema.js的程式碼,如下:


import {
  GraphQLSchema,
  GraphQLObjectType
} from 'graphql';
// 引入 type 
import {info, infos} from './info'
import {student} from './student'

// 建立 schema
export default new GraphQLSchema({
  query: new GraphQLObjectType({
    name: 'Queries',
    fields: {
      infos,
      info,
      student
    }
  })
})

複製程式碼

看到程式碼是如此的清新脫俗,是不是深感欣慰。好了,graophql資料查詢都已經是大概比較完善了。 課程的資料大家可以自己寫一下,或者直接到我的github專案裡面copy過來我就不一一重複的說了。

下面寫一下前端介面是怎麼查詢的,然後讓資料返回瀏覽器展示到頁面的。

前端介面呼叫

public資料夾下面新建一個index.htmljs資料夾css資料夾,然後在js資料夾建立一個index.js, 在css資料夾建立一個index.css,程式碼如下

public/index.html


<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>GraphQL-demo</title>
  <link rel="stylesheet" href="./css/index.css">
</head>
<body>
  <h1 class="app-title">GraphQL-前端demo</h1>
  <div id="app">
    <div class="course list">
      <h3>課程列表</h3>
      <ul id="courseList">
        <li>暫無資料....</li>
      </ul>
    </div>
    <div class="student list">
      <h3>班級學生列表</h3>
      <ul id="studentList">
        <li>暫無資料....</li>
      </ul>
    </div>
  </div>
  <div class="btnbox">
    <div class="btn" id="btn1">點選常規獲取課程列表</div>
    <div class="btn" id="btn2">點選常規獲取班級學生列表</div>
    <div class="btn" id="btn3">點選graphQL一次獲取所有資料,問你怕不怕?</div>
  </div>
  <div class="toast"></div>
  <script src="https://cdn.bootcss.com/jquery/1.10.2/jquery.js"></script>
  <script src="./js/index.js"></script>
</body>
</html>

複製程式碼

我們主要看js請求方式 程式碼如下


window.onload = function () {

  $('#btn2').click(function() {
    $.ajax({
      url: '/student',
      data: {},
      success:function (res){
        if (res.success) {
          renderStudent (res.data)
        }
      }
    })
  })

  $('#btn1').click(function() {
    $.ajax({
      url: '/course',
      data: {},
      success:function (res){
        if (res.success) {
          renderCourse(res.data)
        }
      }
    })
  })

  function renderStudent (data) {
    var str = ''
    data.forEach(function(item) {
      str += '<li>姓名:'+item.name+',性別:'+item.sex+',年齡:'+item.age+'</li>'
    })
    $('#studentList').html(str)
  }

  function renderCourse (data) {
    var str = ''
    data.forEach(function(item) {
      str += '<li>課程:'+item.title+',簡介:'+item.desc+'</li>'
    })
    $('#courseList').html(str)
  }
  
  // 請求看query引數就可以了,跟查詢介面的引數差不多

  $('#btn3').click(function() {
    $.ajax({
      url: '/graphql',
      data: {
        query: `query{
          student{
            _id
            name
            sex
            age
          }
          course{
            title
            desc
          }
        }`
      },
      success:function (res){
        renderStudent (res.data.student)
        renderCourse (res.data.course)
      }
    })
  })
}

複製程式碼

css的程式碼 我就不貼出來啦。大家可以去專案直接拿嘛。

所有東西都已經完成之後,重新啟動node服務,然後訪問,http://localhost:4000/就會看到如下介面。介面醜,沒什麼設計美化細胞,求輕噴~~~~

GraphQL 搭配 Koa 最佳入門實踐

操作點選之後就會想第二張圖一樣了。

所有效果都出來了,本篇文章也就到此結束了。

附上專案地址: github.com/naihe138/Gr…

ps:喜歡的話丟一個小星星(star)給我嘛

相關文章