GraphQL教程四:使用MongoDB作为数据存储层


在前面都是直接在schema.js种定义了一些固定值作为数据源。在真实的项目中,肯定要有一个数据存储层的,下面来说说如何使用MongoDB作为数据存储层。

GraphQL系列教程:

注册MongoDB账号

在这个教程中,使用MongoDB的免费账号就足够了:

GraphQL教程
GraphQL教程

创建账号成功后,创建一个共享集群(Shared Cluster):

GraphQL教程
GraphQL教程

分别创建Database和Collection:

GraphQL教程
GraphQL教程

保存下MongoDB的访问地址:mongodb+srv://lcoding:[email protected]/lcoding

使用Mongoose

首先安装包:

bash
npm install mongoose

然后就可以连接到MongoDB了。连接字符串可以在这里获取:

GraphQL教程
GraphQL教程

连接MongoDB的代码:

javascript
const mongoose = require('mongoose');

mongoose.connect('mongodb+srv://lcoding.hm1sq.mongodb.net/lcoding', {
    auth: {
      username: "lcoding",
      password: "PASSWORD"
    }, useNewUrlParser:true}).then(
        () => { 
            console.log("Database connected");
        },
        err => { 
            console.log("Error in database connection. ", err);
        }
    );

MongoDB model

接下来就可以创建MongoDB的model了,这些model其实就是建立了和MongoDB数据存储层的连接,之后对这些model的操作会自动被保存/更新到MongoDB中。需要注意这些model和GraphQL中的schema不同,那些schema是为了提供访问GraphQL的类型/关系/入口的。

models/Student.js:

javascript
const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const studentSchema = new Schema({
    name: String, 
    address: String,
    tutorId: String
});

module.exports = mongoose.model('Student', studentSchema);

models/Tutor.js:

javascript
const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const tutorSchema = new Schema({
    name: String
});

module.exports = mongoose.model('Tutor', tutorSchema);

Mutation

所谓mutation,就是对数据的增,删,改等操作。

第一步就是在schema/schema.js中定义对应的操作:

javascript
addTutor: {
    type: TutorType,
    args: {
        name: {type: new GraphQLNonNull(GraphQLString)}
    },
    resolve(parent, args) {
        let tutor = new Tutor({
            name: args.name
        });
        return tutor.save();
    }
},

需要注意的是,通过GraphQLNonNull可以限制用户输入,保证没有空值。

同时,在暴露GraphQLSchema的时候,不仅要声明query,还要声明mutation:

javascript
module.exports = new GraphQLSchema({
    query: RootQuery,
    mutation: Mutation
});

这样就可以进行这样的插入操作了:

graphql
mutation {
  addTutor(name: "Dr Tang") {
    name
    id
  }
}

其返回值类似于:

``` json
{
  "data": {
    "addTutor": {
      "name": "Dr Tang",
      "id": "616e95e47a7fbbbe6d5b0afb"
    }
  }
}

如果到MongoDB中查看,也能看到对应的记录:

GraphQL教程
GraphQL教程

对于addStudent,则需要通过tutorId连接到Tutor:

javascript
addStudent: {
    type: StudentType,
    args: {
        name: {type: new GraphQLNonNull(GraphQLString)},
        address: {type: new GraphQLNonNull(GraphQLString)},
        tutorId: {type: new GraphQLNonNull(GraphQLID)}
    },
    resolve(parent, args) {
        let student = new Student({
            name: args.name,
            address: args.address,
            tutorId: args.tutorId
        });
        return student.save();
    }
}

在发出请求的时候是这样的,需要注意tutorId目前只能通过MongoDB web UI来获取:

json
mutation {
  addStudent(name:"Chole",address:"11 Fake Street",tutorId:"616e95e47a7fbbbe6d5b0afb") {
    name
    address
  }
}

更新resolve方法

首先来看看StudentType的定义, 需要注意的是,在resolve方法中,使用的是parent.tutorId,而不是args.id,同时调用的是Tutor.findById,而不是Student.findById

javascript
const StudentType = new GraphQLObjectType({
    name: 'Student',
    fields: () => ({
        id: {type: GraphQLID},
        name: {type: GraphQLString},
        address: {type: GraphQLString},
        tutor: {
            type: TutorType,
            resolve(parent, args) {
                return Tutor.findById(parent.tutorId)
            }
        }
    })
});

这样就可以查询学生及相关tutor的信息了:

graphql
{
  students {
    name
    address
    tutor {
      name
    }
  }
}

这个查询可以返回所有相关的学生信息:

json
{
  "data": {
    "students": [
      {
        "name": "Lucas",
        "address": "1 Fake Street",
        "tutor": {
          "name": "Lucas"
        }
      },
      {
        "name": "Paul",
        "address": "2 Fake Street",
        "tutor": {
          "name": "Paul"
        }
      },
      {
        "name": "Emily",
        "address": "3 Fake Street",
        "tutor": {
          "name": "Emily"
        }
      },
      {
        "name": "Lucy",
        "address": "4 Fake Street",
        "tutor": {
          "name": "Lucy"
        }
      },
      {
        "name": "Jess",
        "address": "5 Fake Street",
        "tutor": {
          "name": "Jess"
        }
      },
      {
        "name": "Jess",
        "address": "5 Fake Street",
        "tutor": {
          "name": "Jess"
        }
      },
      {
        "name": "Steve",
        "address": "6 Fake Street",
        "tutor": {
          "name": "Steve"
        }
      },
      {
        "name": "Luke",
        "address": "7 Fake Street",
        "tutor": {
          "name": "Luke"
        }
      },
      {
        "name": "Dan",
        "address": "8 Fake Street",
        "tutor": {
          "name": "Dan"
        }
      },
      {
        "name": "Ned",
        "address": "9 Fake Street",
        "tutor": {
          "name": "Ned"
        }
      },
      {
        "name": "Tom",
        "address": "10 Fake Street",
        "tutor": {
          "name": "Tom"
        }
      },
      {
        "name": "Chole",
        "address": "11 Fake Street",
        "tutor": {
          "name": "Chole"
        }
      }
    ]
  }
}

与此类似,TutorType中的resolve方法:

javascript
resolve(parent, args) {
    return Student.find({tutorId: parent.id})
}

在RootQuery中student及students两个入口:

javascript
student: {
    type: StudentType,
    args: {id: {type: GraphQLID}},
    resolve(parent, args) {
        return Student.findById(args.id)
    }
},
students: {
    type: new GraphQLList(StudentType),
    resolve(parent, args) {
        return Student.find({});
    }
}

tutor和tutors两个入口的定义类似。


文章作者: 逻思
版权声明: 本博客所有文章除特別声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 逻思 !