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
CRUD
When 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 1980
by British technical writers in the early years James Martin
for applications that create, read, update, and delete data. Although this term has been around for 25
years, 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 CRUD
applications 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 Noted
application will follow the CRUD
pattern. Users will be able to create, read, update and delete their own notes. In this chapter, we will realize API
the basic CRUD
functions by connecting the parser and the database .
Separate our GraphQL model and parser
Currently, our src/index.js
file is the location of the structure and parser Express/Apollo
used 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 src
create a src/schema.js
new file named in the folder, and then move typeDefs
the structure content found in the variable to this file. To do this, we also need to import apollo-server-express
the gql
pattern language that comes with the package and use Node
to export our pattern as a module.exports
method.
First, let's move the GraphQL
pattern to its own file.
During this process, we can also use to delete hello
queries, 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.js
file, and the apollo server express
delete introduced gql
, as follows:
const { ApolloServer } = require('apollo-server-express');
const typeDefs = require('./schema');
Now that we have GraphQL
isolated the module in its own file, let's GraphQL
do something similar for the parser code. Our parser code will contain most of our API
logic, so first we will create a folder to store this code, called the parser. In the src/resolvers
catalog, we started from three src/resolvers/index.js
documents: , src/resolvers/query.js
and src/resolvers/mutation.js
. Similar to the pattern we followed in the database module, the src/resolvers/index.js
file 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 API
set 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.js
file:
module.exports = {
newNote: async (parent, args) => {
return await models.Note.create({
content: args.content,
author: 'Adam Scott'
});
}
}
Next, the server src/index.js
imports 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 Server
the 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.js
the Apollo Server
startup 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.js
performing 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.js
file:
module.exports = {
newNote: async (parent, args, { models }) => {
return await models.Note.create({
content: args.content,
author: 'Adam Scott'
});
}
}
Now, our src/index.js
file 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 CRUD
operation. 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 ID
parameter 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 API
will return a boolean value true
to notify us that the note was deleted successfully.
Update src/schema.js
modify 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 CRUD
operations.
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 Mongoose
the findOneAndRemove
method and ID
pass 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.exports
objects 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 Playground
run our modifications in. In Playground
the 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, true
the response you should receive :
{
"data": {
"deleteNote": true
}
}
If the transfer does not exist ID
, you will receive a " deleteNote
" false
response: .
With our delete function, let's write our
updateNote
modify. To this end, we will use Mongoose
the
findOneAndUpdate
method. 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
:, true
which 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 Playground
to try our updateNote
modification. In Playground
the new tab page above, use id
and content
parameter to write a modification:
mutation {
updateNote(
id: "5c7d1f0a31191c4413edba9d",
content: "This is an updated note!"
){
id
content
}
}
If our modification works as expected, the GraphQL
response 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 API
China has a complete CRUD
function.
Date and time
When creating a database structure, we require Mongoose
automatic 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 createdAt
and updatedAt
fields to store these values.
You may recall that GraphQL
allow the use of the default type String
, Boolean
, Int
, Float
and ID
. Unfortunately, GraphQL
there is no built-in date variable type. We can use String
types, but this means that we will not be able to use GraphQL
the 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 GQL
update src/schema.js
the GraphQL
mode in by adding a custom variable at the top of the string literal :
module.exports = gql` scalar DateTime
... `;
Now, add the createdAt
sum updatedAt
field:
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-date
Software package. Now, we will DateTime
add validation to any parser function that requests a value of type .
In the src/resolvers/index.js
file, import the package, and then add the DateTime
value 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 Playground
and 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 createdAt
sum updatedAt
field is a formatted date and time. As 6-1
shown in the figure, this type of document indicates that it is " UTC
a string of date and time".
Figure 6-1
. Our structure now has DateTime
type
To test this, let's GraphQL Playground
write a newNote
modification 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 createdAt
sum updatedAt
value ISO
. If we run another updateNote
modification to the same character, we will see a createdAt
different updatedAt
value from the date .
For more information on defining and verifying custom variable types, it is recommended to read Apollo Server
the "Custom Variables and Enumerations" document.
in conclusion
In this chapter, we API
added create, read, update and delete ( CRUD
) functions. CRUD
It 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 API
add 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.