服务端采用 apollo-server 实现
客户端采用 apollo-clint + vue3.0 实现
数据库采用 notarealdb 模拟
mkdir session2
cd session2
yarn init
# 根据提示补充完整信息
yarn add apollo-server graphql notarealdb
# apollo-server 可以直接启动服务端,不需要使用服务端框架
# graphql 必备的
# notarealdb 通过JSON文件 模拟数据库 也可以使用真实的数据库
"scripts": {
"dev": "nodemon ."
},
{
"name": "session2",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"scripts": {
"dev": "node ."// "nodemon ." // 需要安装yarn add nodemon --save 或者全局安装 作用是修改了代码自动重启服务
},
"dependencies": {
"apollo-server": "^3.5.0",
"graphql": "^16.0.1",
"notarealdb": "^0.2.2"
}
}
// students.json
[
{
"id": "Sy0bHHgqY",
"name": "张三",
"age": 18,
"sex": "男",
"classID": 1
},
{
"id": "SJGB8re5Y",
"name": "李四",
"age": 20,
"sex": "男",
"classID": 2
},
{
"id": "B1MnIrecF",
"name": "赵六",
"age": 22,
"sex": "男",
"classID": 3
},
{
"id": "BkXePHx9K",
"name": "王五",
"age": 22,
"sex": "男",
"classID": 3
}
]
// class.json
[{
"id":1,
"name":"col_01",
"description":"三年一班"
},{
"id":2,
"name":"col_02",
"description":"三年二班"
},{
"id":3,
"name":"col_03",
"description":"三年三班"
}]
// db.js
const { DataStore } = require('notarealdb');
const store = new DataStore('./data'); // 这里是因为将之前的两个json文件放到了data文件夹中
module.exports = {
students:store.collection('students'),
class:store.collection('class')
};
# session.gql
# # 是注释
# 自定义类型
type Student{
id:ID! # !表示必须有的字段
name:String
age: Int
sex: String
class:Class # 自定义类型中的查询方法
}
# 自定义类型
type Class{
id:Int!
name:String
description:String
}
# 通用的查询方法定义
type Query{
students:[Student]
studentsByName(id:String):Student
classes:[Class]
classById(id:ID):Class
}
# 自定义的参数类型 可以做参数校验
input InputStudent{
name:String
age:Int
sex:String
classID:Int
}
# 增 删 改 都在这里定义
type Mutation{
createStudent(s:InputStudent):String
updateStudent(s:InputStudent):[Student]
}
// session.js
const db = require('../db')
// 定义查询函数 与schema 中的Query对应 schema中有的可以不在此处实现 但是这里出现的必须是在schema中定义了的
const Query = {
students:()=> {
return db.students.list();
},
studentsByName:(root,args,context,info)=>{
return db.students.get(args.id)
},
classes:(root,args,context,info)=>{
return db.class.list();
}
}
// 当自定义的类型中有查询 需要定义该类型的查询 这里的Student 与 schema 中的 Student类型对应
const Student = {
class:(root,args,context,info)=>{
return db.class.get(root.classID)
}
}
// 修改 与schema中的Mutation对应
const Mutation = {
createStudent:(root,{s},context,info)=>{
return db.students.create(s)
},
updateStudent:(root,{s},context,info)=>{
db.students.update(s)
return db.students.list();
}
}
module.exports = {
Query,
Student,
Mutation
}
const fs = require('fs')
// 读取schema
const typeDefs = fs.readFileSync("./gql/session.gql", { encoding: 'utf-8' })
// 导入resolver
const resolvers = require('./resolvers/session1')
const { ApolloServer } = require('apollo-server');
const { ApolloServerPluginLandingPageGraphQLPlayground } = require("apollo-server-core");
// 生成服务端应用
const app = new ApolloServer({
typeDefs,
resolvers,
plugins: [
ApolloServerPluginLandingPageGraphQLPlayground // 开启测试页面
],
})
// 启动服务
app.listen(8000).then(({url}) => {
console.log(url);
})
# 在index.js 所在的文件夹中执行
yarn dev
# 查询
query{
students{
id # 这里根据需要的字段查询
name
age
sex
class{ # 因为class字段返回的是Class类 所以也需要指定字段
id
description
}
}
}
# 也可以多个查询一起
query{
students{ #
name
class{
description
}
},
classes{ #
id
description
}
}
# mutation
mutation{
createStudent(s:{name:"张三",age:10,sex:"男",classID:1})
# 传参必须是k:v方式 参数类型是对象 不是所有的参数字段都是必须的
# 返回值是字符串,所以不需要选字段
}
客户端使用vue3.x创建
vue create graphql
# 根据提示选择 我这里选择了vue3 default
yarn add graphql graphql-tag apollo-boost
// vue.config.js
module.exports ={
devServer: {
proxy:{
'/api':{
target:"http://127.0.0.1:8000", // 这里是服务端的地址
changeOrigin:true,
pathRewrite:{
'^/graphql':'/graphql' // 这里是代理uri:正式的uri 真实的就是在这个路由,不是调试页面的路由
}
}
}
}
}
// script/graphql.js
import { HttpLink } from "apollo-link-http"
import ApolloClient from "apollo-client"
import { InMemoryCache } from 'apollo-cache-inmemory'
import gql from "graphql-tag";
const httpLink = new HttpLink({
uri: "/api/graphql"
})
/* 这样创建的客户端 同样的请求会使用缓存,得不到最新的数据 如果是只需要初始化那就可以使用这个
const apolloClient = new ApolloClient({
link: httpLink,
cache: new InMemoryCache(),
connectToDevTools: true,
})
*/
/* 构造新的客户端 */
function getClint(){
return new ApolloClient({
link: httpLink,
cache: new InMemoryCache(),
connectToDevTools: true,
})
}
/**用于查询的 */
const clintQuery = (queryStr) => {
/**每次都是用新的客户端 */
return getClint().query({ query: gql(queryStr) })
}
/**用于修改的 用于修改的其实可以使用单例客户端 */
const clintMutation = (mutation, variables) => {
return getClint().mutate({ mutation: gql(mutation), variables: variables })
}
export { clintQuery, clintMutation }
<template>
<div>
<div>
<div class="input">
Mutation
<div>
<label for="name">姓名:</label>
<input type="text" id="name" v-model="stuName" />
</div>
<div>
<label for="age">年龄:</label>
<input type="number" id="age" v-model="age" />
</div>
<div class="slt">
<span>
<label for="sex">性别:</label>
<select id="sex" name="sex" v-model="sex">
<option value="男">男</option>
<option value="女">女</option>
</select>
</span>
<span>
<label for="cls">班级:</label>
<select id="cls" v-model="cls">
<option :value="item.id" v-for="item in classes" :key="item.id">
{{ item.description }}
</option>
</select>
</span>
</div>
<input
type="button"
value="创建"
@click="
createStudent();
reset();
"
/>
</div>
</div>
<hr>
Query
<table class="stu">
<thead>
<tr>
<th>ID</th>
<th>姓名</th>
<th>班级</th>
</tr>
</thead>
<tbody>
<transition-group name="stuList">
<tr v-for="item in students" :key="item.id">
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
<td>{{ item.class }}</td>
</tr>
</transition-group>
</tbody>
</table>
<div></div>
</div>
</template>
<script>
import { clintQuery, clintMutation } from "../script/graphql";
export default {
name: "HelloWorld",
props: {
msg: String,
},
data() {
return {
students: [],
stuName: "",
age: 0,
sex: "男",
cls: "",
classes: [],
};
},
components: [],
methods: {
getStudents: function () {
// 不要使用箭头函数 否则无法获取this
let that = this; // 由于在异步中 this 重置,所以提前将this赋值给that 以便后面获取到数据
// 调用封装好的graphql 客户端发送请求
clintQuery(`
query {
students {
id
name
sex
class {
description
}
}
}
`).then(function (rst) {
let students = [];
for (let i = 0; i < rst.data.students.length; i++) {
students.push({
id: rst.data.students[i].id,
name: rst.data.students[i].name,
sex: rst.data.students[i].sex,
class: rst.data.students[i].class.description,
});
}
that.students = students;
});
},
getClasses: function () {
let that = this;
clintQuery(`
query {
classes {
id
description
}
}
`).then(function (rst) {
that.classes = rst.data.classes;
});
},
createStudent: function () {
let that = this;
// 第一个参数是执行的graphql查询字符串,第二个参数是传参
clintMutation(
`
mutation createStudent($name:String,$age:Int,$sex:String,$classID:Int) {
createStudent(s:{name:$name,age:$age,classID:$classID,sex:$sex})
}
`,
{
name: that.stuName,
age: that.age,
sex: that.sex,
classID: that.cls,
}
).then(() => that.getStudents());
},
reset: function () {
this.stuName = "";
this.age = 0;
this.cls = "";
this.sex = "";
},
},
mounted() {
this.getStudents();
this.getClasses();
},
watch: {},
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.stu {
width: 400px;
margin: 0 auto;
}
.stu tbody{
width:100%;
height: 300px;
overflow: auto;
}
.stu tbody tr {
height: 20px;
}
.stu thead {
background-color: #b6b6b6;
}
.stu th {
width: 100px;
}
tbody tr:nth-child(2n-1) {
background-color: rgba(133, 158, 133,.6);
}
.input {
width: 400px;
margin: 0 auto;
}
.input .slt {
margin: 0 auto;
display: flex;
flex-direction: row;
justify-content: space-around;
width: 56%;
}
.stuList-item{
/* opacity: 1; */
transition: all 0.8s ease;
}
.stuList-enter-active,
.stuList-leave-active {
transition: all 0.5s ease;
}
.stuList-enter_from,
.stuList-leave-to{
opacity: 0;
transform: translateY(10px);
}
</style>
yarn serve
http://localhost:8080/
最后的最后 所有的代码都放在了 https://gitee.com/mslsy/graphql