翻译 | 《JavaScript Everywhere》第6章 CRUD操作(^\_^)

写在最后面

大家好呀,我是毛小悠,是一位前端开发工程师。正在翻译一本英文技术书籍。

为了进步大家的浏览体验,对语句的构造和内容略有调整。如果发现本文中有存在瑕疵的中央,或者你有任何意见或者倡议,能够在评论区留言,或者加我的微信:code\_maomao,欢送互相沟通交流学习。

(゚∀゚)..:\*☆哎哟不错哦

第6章 CRUD操作

第一次听到“ CRUD应用程序”一词时,我谬误地认为它指的是用来执行一些恶浊或辣手的应用程序。诚然,“ CRUD”听起来如同是指从鞋底刮下来的货色。实际上,首字母缩写词是在1980年代初期由英国技术作家James Martin援用的,用于创立、读取、更新和删除数据的应用程序。只管这个术语曾经存在了25年之久,但它依然实用于当今开发的许多应用程序。考虑一下你每天与之交互的应用程序,例如待办事项列表,电子表格,内容管理系统,文本编辑器,社交媒体网站,用于及其他几个应用程序,很可能其中许多应用程序都属于CRUD应用程序格局。用户可用于创立一些数据,拜访或读取数据,并可用于更新或删除该数据。

咱们的Noted应用程序将遵循CRUD模式。用户将可能创立、浏览、更新和删除本人的笔记。在本章中,咱们将通过连贯解析器和数据库来实现API的根本CRUD性能。

拆散咱们的GraphQL模式和解析器

以后,咱们的src/index.js文件是Express/Apollo服务器代码用于及API的构造和解析器的所在地。

可用于设想,随着咱们代码库的增长,这可能会变得有些蠢笨。在此之前,让咱们花一些工夫进行较小的重构,用于将咱们的构造、解析器和服务器代码离开。首先,咱们将在src文件夹中创立一个名为src/schema.js的新文件,而后将在typeDefs变量中找到的构造内容挪动到该文件中。为此,咱们还须要导入apollo-server-express软件包随附的gql模式语言,并应用Node将咱们的模式导出为module.exports形式。

首先,让咱们将GraphQL模式挪动到它本人的文件中。

在此过程中,咱们还可用于删除hello查问,这在最终应用程序中将不再被须要:

const { gql } = require('apollo-server-express');module.exports = gql` type Note { id: ID!   content: String!   author: String!  }  type Query { notes: [Note!]!   note(id: ID!): Note!  }  type Mutation {   newNote(content: String!): Note!  } `; 

咱们当初通过导入这个内部构造文件,可用于更新src/index.js文件,并从apollo server express中删除导入的gql,如下所示:

const { ApolloServer } = require('apollo-server-express');const typeDefs = require('./schema'); 

当初咱们曾经将GraphQL模块隔离到了本人的文件中,让咱们为GraphQL解析器代码做相似的事件。咱们的解析器代码将蕴含咱们绝大多数API逻辑,因而首先咱们将创立一个文件夹来存储此代码,称为解析器。在src/resolvers目录中,咱们将从三个文件开始:src/resolvers/index.jssrc/resolvers/query.jssrc/resolvers/mutation.js。与咱们在数据库模块中遵循的模式类似,src/resolvers/index.js文件将用于将解析器代码导入到单个导出模块中。持续设置该文件,如下所示:

const Query = require('./query');const Mutation = require('./mutation');module.exports = {  Query,  Mutation}; 

当初,你可用于为API查问代码设置src/resolvers/query.js

module.exports = {  notes: async () => {   return await models.Note.find()  },  note: async (parent, args) => {   return await models.Note.findById(args.id);  }} 

而后将批改代码移至src/resolvers/mutation.js文件:

module.exports = {  newNote: async (parent, args) => {   return await models.Note.create({    content: args.content,    author: 'Adam Scott'   });  }} 

接下来,服务器通过将用于上行增加到src/index.js文件来导入解析器代码:

const resolvers = require('./resolvers');

重构解析器的最初一步是将它们连贯到咱们的数据库模块。你可能曾经留神到,咱们的解析器模块援用了这些模块,然而无法访问它们。要解决此问题,咱们将应用Apollo Server中调用的概念context,它使咱们可用于依据每个申请将特定信息从服务器代码传递到单个解析器中。目前,这可能有点过头,然而对于将用户身份验证合并到咱们的应用程序中将很有用。为此,咱们将更新位于str/index.jsApollo Server启动代码,通过一个返回咱们的数据库模块的上下文函数。

// Apollo Server setupconst server = new ApolloServer({  typeDefs,  resolvers,  context: () => {   // Add the db models to the context   return { models };  } }); 

当初,咱们通过将{modules}增加为每个函数的第三个参数,而后更新每个解析器来利用此上下文函数。

src/resolvers/query.js中执行用于下操作:

module.exports = {  notes: async (parent, args, { models }) => {   return await models.Note.find()  },  note: async (parent, args, { models }) => {   return await models.Note.findById(args.id);  }} 

将批改代码移到src/resolvers/mutation.js文件中:

 module.exports = {  newNote: async (parent, args, { models }) => {    return await models.Note.create({      content: args.content,      author: 'Adam Scott'    });  }} 

当初,咱们的src/index.js文件将简化如下:

 const express = require('express');const { ApolloServer } = require('apollo-server-express');require('dotenv').config();// Local module importsconst db = require('./db');const models = require('./models');const typeDefs = require('./schema');const resolvers = require('./resolvers');// Run our server on a port specified in our .env file or port 4000const port = process.env.PORT || 4000;const DB_HOST = process.env.DB_HOST;const app = express();db.connect(DB_HOST);// Apollo Server setupconst server = new ApolloServer({  typeDefs,  resolvers,  context: () => {    // Add the db models to the context    return { models };  }});// Apply the Apollo GraphQL middleware and set the path to /apiserver.applyMiddleware({ app, path: '/api' });app.listen({ port }, () =>  console.log(    `GraphQL Server running at http://localhost:${port}${server.graphqlPath}`  )); 

编写咱们的GraphQL CRUD模式

既然咱们曾经重构了代码进步了灵活性,那么让咱们开始实现CRUD操作。咱们曾经可能创立和浏览笔记,这使咱们可能实现更新和删除性能。首先,咱们要更新构造。

因为更新和删除操作将更改咱们的数据,因而它们将会被批改。咱们更新笔记将须要一个ID参数来定位笔记用于及新笔记的内容。而后,更新查问将返回新更新的笔记。对于咱们的删除操作,咱们的API将返回布尔值true,用于告诉咱们笔记删除胜利。

更新src/schema.js中的批改构造如下:

 type Mutation {  newNote(content: String!): Note!  updateNote(id: ID!, content: String!): Note!  deleteNote(id: ID!): Boolean!} 

有了这些附加性能,咱们的构造当初可用于执行CRUD操作了。

CRUD解析器

有了咱们的构造后,当初能够更新解析器来删除或更新笔记。让咱们批改咱们的deleteNote。要删除笔记,咱们将应用MongoosefindOneAndRemove办法,并将要删除的商品的ID传递给它。如果找到并删除了咱们的商品,咱们将向客户端返回true,然而如果咱们无奈删除商品,则将返回false

src/resolvers/mutation.js中,在模块内增加module.exports 对象:

 deleteNote: async (parent, { id }, { models }) => {  try {    await models.Note.findOneAndRemove({ _id: id});    return true;  } catch (err) {    return false;  }}, 

当初咱们能够在GraphQL Playground中运行咱们的批改。在Playground的新标签中,输出以下批改,并确保应用数据库中笔记之一中的ID

mutation {  deleteNote(id: "5c7d1aacd960e03928804308")} 

如果笔记被胜利删除,你应该收到true的响应:

{  "data": {    "deleteNote": true  }} 

如果传递不存在的ID,则会收到“ deleteNote”响应:false

有了咱们的删除性能后,让咱们编写咱们的

updateNote批改。为此,咱们将应用Mongoose

findOneAndUpdate办法。该办法将应用查问的初始参数在数据库中找到正确的笔记,而后是第二个参数,在此咱们将设置新笔记的内容。最初,咱们将传递第三个参数newtrue,它批示数据库将更新后的笔记内容返回给咱们。

src/resolvers/mutation.js中,在模块中增加module.exports

updateNote: async (parent, { content, id }, { models }) => {  return await models.Note.findOneAndUpdate(    {      _id: id,    },    {      $set: {        content      }    },    {      new: true    }  );}, 

当初,咱们可用于在浏览器中拜访GraphQL Playground来尝试咱们的updateNote批改。在Playground上的新标签页中,应用idcontent参数编写一个批改:

mutation {  updateNote(    id: "5c7d1f0a31191c4413edba9d",    content: "This is an updated note!"  ){    id    content  }} 

如果咱们的批改按预期工作,则GraphQL响应应如下所示:

{  "data": {    "updateNote": {      "id": "5c7d1f0a31191c4413edba9d",      "content": "This is an updated note!"    }  }} 

如果咱们传递了谬误的ID,则响应将失败,并且咱们将收到外部服务器谬误,并带有谬误更新笔记的音讯。

当初,咱们可用于创立、浏览、更新和删除笔记。这样,咱们的API中就具备残缺的CRUD性能。

日期和工夫

创立数据库构造时,咱们要求Mongoose主动存储工夫戳用于记录在数据库中创立和更新条目标工夫。此信息在咱们的应用程序中将很有用,因为它使咱们可用于在用户界面中向用户显示笔记的创立工夫或最初编辑工夫。让咱们增加createdAtupdatedAt字段来寄存这些值。

你可能还记得GraphQL容许应用默认类型的StringBooleanIntFloatID。可怜的是,GraphQL没有内置的日期变量类型。咱们能够应用String类型,然而这意味着咱们将不能利用GraphQL提供的类型验证,从而确保咱们输出的日期和工夫内容肯定是日期和工夫。相同,咱们能够创立一个自定义的变量类型。

自定义类型容许咱们定义一个新类型,并针对申请该类型数据的每个查问和批改进行验证。

让咱们通过在GQL字符串文字的顶部增加一个自定义变量来更新src/schema.js中的GraphQL模式:

 module.exports = gql` scalar DateTime  ... `; 

当初,增加createdAtupdatedAt字段:

 type Note {  id: ID!  content: String!  author: String!  createdAt: DateTime!  updatedAt: DateTime!} 

最初一步是验证此新类型。

尽管咱们能够编写本人的验证,但以后对于咱们的用例,咱们将应用

graphql-iso-date软件包。当初,咱们将向任何申请类型为DateTime的值的解析器函数增加验证。

src/resolvers/index.js文件中,导入包,而后将DateTime值增加到导出的解析器,如下所示:

const Query = require('./query');const Mutation = require('./mutation');const { GraphQLDateTime } = require('graphql-iso-date');module.exports = {  Query,  Mutation,  DateTime: GraphQLDateTime}; 

当初,如果咱们在浏览器中拜访GraphQL Playground并刷新页面,则能够验证咱们的自定义类型是否按预期工作。如果申请咱们的字段,咱们能够看到createdAtupdatedAt字段是有格局的日期工夫。如图6-1所示,此类型的文档指出它是“ UTC的日期工夫字符串”。

6-1。咱们的构造当初具备DateTime类型

为了测试这一点,让咱们在GraphQL Playground中编写一个newNote批改,其中包含咱们的日期字段:

 mutation {  newNote (content: "This is a note with a custom type!") {    content    author    id    createdAt    updatedAt  }} 

这将返回createdAtupdatedAt数值为ISO格局的日期。如果咱们再运行一个updateNote对同一个字符批改,咱们将看到一个和createdAt日期不同的updatedAt值。

无关定义和验证自定义变量类型的更多信息,倡议浏览Apollo Server的“自定义变量和枚举”文档。

论断

在本章中,咱们向API增加了创立、读取、更新和删除(CRUD)性能。CRUD是许多应用程序应用的一种十分常见的模式。我心愿你查看你每天应用的应用程序,并思考它们的数据如何应用这种模式。在下一章中,咱们将向API增加性能用于创立和验证用户帐户。

如果有了解不到位的中央,欢送大家纠错。如果感觉还能够,麻烦您点赞珍藏或者分享一下,心愿能够帮到更多人。