Translation | "JavaScript Everywhere" Chapter 6 CRUD Operations (^\_^)

Translation | "JavaScript Everywhere" Chapter 6 CRUD Operations ( _ )

Write at the top

Hello everyone, I am Mao Xiaoyou, a front-end development engineer. An English technical book is being translated.

In order to improve everyone's reading experience, the structure and content of sentences have been slightly adjusted. If you find any flaws in this article, or if you have any comments or suggestions, you can leave a message in the comment area, or add my WeChat: code_maomao, welcome to communicate and learn from each other.

(σ゚∀゚)σ…:*☆Oh, good

Chapter 6 CRUD Operations

CRUDWhen I first heard the term " application", I mistakenly thought it was referring to some dirty or tricky applications. Of course, " CRUD" sounds like it refers to something scraped off the sole. In fact, the acronym was quoted 1980by British technical writers in the early years James Martinfor applications that create, read, update, and delete data. Although this term has been around for 25years, it still applies to many applications developed today. Consider the applications you interact with every day, such as to-do lists, spreadsheets, content management systems, text editors, social media sites, and several other applications. It is likely that many of these applications are CRUDapplications Program format. Users can be used to create some data, access or read data, and can be used to update or delete the data.

Our Notedapplication will follow the CRUDpattern. Users will be able to create, read, update and delete their own notes. In this chapter, we will realize APIthe basic CRUDfunctions by connecting the parser and the database .

Separate our GraphQL model and parser

Currently, our src/index.jsfile is the location of the structure and parser Express/Apolloused by the server code API.

It can be used to imagine that as our code base grows, this may become a bit clumsy. Before that, let's spend some time on a minor refactoring to separate our structure, parser, and server code. First, we will srccreate a src/schema.jsnew file named in the folder, and then move typeDefsthe structure content found in the variable to this file. To do this, we also need to import apollo-server-expressthe gqlpattern language that comes with the package and use Nodeto export our pattern as a module.exportsmethod.

First, let's move the GraphQLpattern to its own file.

During this process, we can also use to delete helloqueries, which will no longer be needed in the final application:

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!
  } `; 

We now document by introducing this external structure, can be used to update the src/index.jsfile, and the apollo server expressdelete introduced gql, as follows:

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

Now that we have GraphQLisolated the module in its own file, let's GraphQLdo something similar for the parser code. Our parser code will contain most of our APIlogic, so first we will create a folder to store this code, called the parser. In the src/resolverscatalog, we started from three src/resolvers/index.jsdocuments: , src/resolvers/query.jsand src/resolvers/mutation.js. Similar to the pattern we followed in the database module, the src/resolvers/index.jsfile will be used to import the parser code into a single export module. Continue to set up the file as follows:

const Query = require('./query');
const Mutation = require('./mutation');

module.exports = {
  Query,
  Mutation
}; 

Now, you can use to APIset the query code src/resolvers/query.js:

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

Then move the modified code to the src/resolvers/mutation.jsfile:

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

Next, the server src/index.jsimports the parser code by adding the use line to the file:

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

The final step in refactoring the parsers is to connect them to our database module. You may have noticed that our parser module references these modules, but cannot access them. To solve this problem, we will use Apollo Serverthe concept of invocation context, which allows us to pass specific information from the server code to a single parser based on each request. For now, this may be a bit overkill, but it will be useful for incorporating user authentication into our application. To do this, we will update str/index.jsthe Apollo Serverstartup code located at , through a context function that returns to our database module.

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

Now, we take advantage of this context function by adding { } as the third parameter of each function, and then updating each parser. modules

In src/resolvers/query.jsperforming the operations for:

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

Move the modified code to the src/resolvers/mutation.jsfile:

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

Now, our src/index.jsfile will be simplified as follows:

 const express = require('express');
const { ApolloServer } = require('apollo-server-express');
require('dotenv').config();

// Local module imports
const 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 4000
const port = process.env.PORT || 4000;
const DB_HOST = process.env.DB_HOST;

const app = express();

db.connect(DB_HOST);

// Apollo Server setup
const 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 /api
server.applyMiddleware({ app, path: '/api' });

app.listen({ port }, () =>
  console.log(
    `GraphQL Server running at http://localhost:${port}${server.graphqlPath}`
  )
); 

Write our GraphQL CRUD model

Now that we have refactored the code to increase flexibility, let's start to implement the CRUDoperation. We have been able to create and read notes, which allows us to implement update and delete functions. First, we need to update the structure.

Since the update and delete operations will change our data, they will be modified. We update the note will need a IDparameter to locate the note used for and the content of the new note. Then, the update query will return the newly updated note. For our delete operation, ours APIwill return a boolean value trueto notify us that the note was deleted successfully.

Update src/schema.jsmodify the structure as follows:

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

With these additional features, our structure can now be used to perform CRUDoperations.

CRUD parser

With our structure, the parser can now be updated to delete or update notes. Let us modify ours deleteNote. To delete a note, we will use Mongoosethe findOneAndRemovemethod and IDpass to it the item to be deleted . If our product is found and deleted, we will return to the client true, but if we cannot delete the product, we will return false.

In src/resolvers/mutation.js, add module.exportsobjects in the module :

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

Now we can GraphQL Playgroundrun our modifications in. In Playgroundthe new tab of, enter the following modifications and make sure to use one of the notes in the database ID:

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

If the note is successfully deleted, truethe response you should receive :

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

If the transfer does not exist ID, you will receive a " deleteNote" falseresponse: .

With our delete function, let's write our

updateNotemodify. To this end, we will use Mongoosethe

findOneAndUpdatemethod. This method will use the initial parameters of the query to find the correct note in the database, and then the second parameter, where we will set the content of the new note. Finally, we will pass the third parameter new:, truewhich instructs the database to return the updated note content to us.

In src/resolvers/mutation.js, add in the modulemodule.exports

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

Now, we can use it to visit in the browser GraphQL Playgroundto try our updateNotemodification. In Playgroundthe new tab page above, use idand contentparameter to write a modification:

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

If our modification works as expected, the GraphQLresponse should look like this:

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

If we pass the wrong one ID, the response will fail, and we will receive an internal server error with a message about the wrong update note.

Now we can use it to create, read, update, and delete notes. In this way, our APIChina has a complete CRUDfunction.

Date and time

When creating a database structure, we require Mongooseautomatic storage of timestamps to record the time of creating and updating entries in the database. This information will be useful in our application because it allows us to display the creation time or last editing time of the note to the user in the user interface. Let's add createdAtand updatedAtfields to store these values.

You may recall that GraphQLallow the use of the default type String, Boolean, Int, Floatand ID. Unfortunately, GraphQLthere is no built-in date variable type. We can use Stringtypes, but this means that we will not be able to use GraphQLthe type verification provided to ensure that the date and time content we enter must be date and time. Instead, we can create a custom variable type.

A custom type allows us to define a new type and validate it for every query and modification that requests data of that type.

Let's GQLupdate src/schema.jsthe GraphQLmode in by adding a custom variable at the top of the string literal :

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

Now, add the createdAtsum updatedAtfield:

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

The final step is to verify this new type.

Although we can write our own verification, currently for our use case, we will use

graphql-iso-dateSoftware package. Now, we will DateTimeadd validation to any parser function that requests a value of type .

In the src/resolvers/index.jsfile, import the package, and then add the DateTimevalue to the exported parser as follows:

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

module.exports = {
  Query,
  Mutation,
  DateTime: GraphQLDateTime
}; 

Now, if we visit GraphQL Playgroundand refresh the page in a browser , we can verify that our custom type works as expected. If we request our field, we can see that the createdAtsum updatedAtfield is a formatted date and time. As 6-1shown in the figure, this type of document indicates that it is " UTCa string of date and time".

Figure 6-1. Our structure now has DateTimetype

To test this, let's GraphQL Playgroundwrite a newNotemodification in which includes our date field:

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

This will return the date in the format of the createdAtsum updatedAtvalue ISO. If we run another updateNotemodification to the same character, we will see a createdAtdifferent updatedAtvalue from the date .

For more information on defining and verifying custom variable types, it is recommended to read Apollo Serverthe "Custom Variables and Enumerations" document.

in conclusion

In this chapter, we APIadded create, read, update and delete ( CRUD) functions. CRUDIt is a very common pattern used by many applications. I hope you look at the applications you use every day and consider how their data uses this pattern. In the next chapter, we will APIadd functionality to create and verify user accounts.

If there is any inadequate understanding, please correct me. If you think it's okay, please like to collect or share it, hoping to help more people.

Guess you like

Origin blog.csdn.net/code_maomao/article/details/110045415