preface

Vue + Express +mongodb — Personal full stack Blog project (part 1

Initialize the project structure

First create the directory (I’m creating a new server folder under the entire project file) and initialize the node project

npm init -y
Copy the code

Install express

npm i express
Copy the code

Generate the package.json file and create the entry file app.js in the server directory

const express = require('express');

app = express();

app.listen(3000.() = > {
    console.log('http://localhost:3000');
})
Copy the code

Add startup commands to package.json (personal habit, same as vue run commands) to make it easier to run the project later.

  "scripts": {
    "serve": "nodemon app.js"."test": "echo \"Error: no test specified\" && exit 1"
  },
Copy the code

Connecting to the MongoDB Database

Install MongoDB first: Beginner tutorial MongoDB installation

After installation, start the service according to the method for different platforms.

Then install Mongoose:

npm i mongoose
Copy the code

Mongoose is an object model tool for convenient operation of mongodb in node.js asynchronous environment, which is more convenient to use than the underlying mongodb node driver.

Create a plugins folder in the server directory, and then create the database connection file db.js in the plugins folder

module.exports = app= > {
    const mongoose = require("mongoose")
    mongoose.connect('mongo: / / 127.0.0.1:27017 / vue - blog', {
      useNewUrlParser: true.useUnifiedTopology: true})}Copy the code

Then reference it in the entry file app.js

require('./plugins/db')(app)
Copy the code

Creating a data model

Create a models folder at the root of the project to store the data model, define which pieces of data to store, and what data types to store. For an Article, create a new Article. Js file under the Models folder

const mongoose = require('mongoose')

const schema = new mongoose.Schema({
    / / title
    title: { type: String },
    / / profile
    introduction: { type: String },
    / / cover
    cover: { type: String },
    / / content (markdown)
    body: { type: String },
    //html
    content: { type: String },
    // Contains the tag
    tags: { type: Array },
    / / id tags
    tagid: [{ type: mongoose.SchemaTypes.ObjectId, ref: 'Tag'}].// Update date
    date: { type: String },
    / /
    click: {type: Number},
    // Number of comments
    comment: {type: String},
    / / the author
    author: { type: mongoose.SchemaTypes.ObjectId, ref: 'User'}})// Export the model module
module.exports = mongoose.model('Article', schema)
Copy the code

Establishing a Routing Interface

In the server folder, create a routes folder. In this folder, create two folders, admin and Web, respectively

Background Administrative routing (Generic CURD)

Create a new middleware folder under server for middleware and create a resource. Js file in this folder

You need to install an Inflection package to convert lowercase resource names to uppercase class names

npm i inflection
Copy the code
module.exports = options= > {
  return async (req, res, next) => {
    const modelName = require('inflection').classify(req.params.resource)
    req.Model = require(`.. /models/${modelName}`)
    next()
  }
}
Copy the code

Create a new index.js file in the admin folder

module.exports = app= > {
    const express = require('express')
    const router = express.Router()
    // Create a resource
    router.post('/'.async (req, res) => {
        const model = await req.Model.create(req.body)
        res.send(model)
    })
    // Update resources
    router.put('/:id'.async (req, res) => {
        const model = await req.Model.findByIdAndUpdate(req.params.id, req.body)
        res.send(model)
    })
    // Delete the resource
    router.delete('/:id'.async (req, res) => {
        await req.Model.findByIdAndDelete(req.params.id)
        res.send({
            success: true})})// Resource list
    router.get('/'.async (req, res) => {
        const queryOptions = {}
        const items = await req.Model.find().setOptions(queryOptions).limit(100)
        res.send(items)
    })
    // Resource details
    router.get('/:id'.async (req, res) => {
        const model = await req.Model.findById(req.params.id)
        res.send(model)
    })

    / / middleware
    const resourceMiddleware = require('.. /.. /middleware/resource')
    app.use('/admin/api/rest/:resource', resourceMiddleware(), router)
    
}
Copy the code

Since no route is written to death, such a set of interfaces can be used for adding, deleting, modifying and querying all resources, but some places that need to be queried by association still need to be handled separately

Image upload

Create the uploads folder under the server directory to store the pictures uploaded in the background rich text editor (now we are trying to upload them to Qiuniuyun), and then install multer middleware to handle file uploads:

npm i multer
Copy the code

Initialize the Multer middleware and then add it to the route to upload the image

    const multer = require('multer')
    const upload = multer({dest: __dirname + '/.. /.. /uploads'})
    app.post('/admin/api/upload',upload.single('file'), async(req, res) => {
        const file = req.file
        file.url = `http://localhost:3000/uploads/${file.filename}`
        res.send(file)
    })
Copy the code

Host static files in app.js

app.use('/uploads', express.static(__dirname + '/uploads'))
Copy the code

Foreground blog routing

Normal resource list, resource details, but much nonsense, mainly about the article search and user login implementation

Use regular fuzzy queries

    // Search for some articles
    router.get('/search'.(req, res) = > {
        let regexp = new RegExp(req.query.key, 'i')

        Article.find(
            {
                $or: [{title: { $regex: regexp } },
                    { introduction: { $regex: regexp } },
                    { content: { $regex: regexp } },
                    { tags: { $regex: regexp } },
                    { date: { $regex: regexp } },
                ]
            }, (err, doc) = > {
                if (err) {
                    console.log(err)
                    res.send({
                        code: 400.msg: "Query failed"})}if (doc) {
                    res.send({
                        code: 200.msg: "Query successful".data: doc
                    })
                }
            })
    })
Copy the code

In terms of login, the first thing to notice is that to hash passwords in the user model, you need to install a bcrypt module

npm i bcrypt
Copy the code

The second parameter of hashSync() is the hash index. The higher the hash index is, the safer and more time-consuming it is. Generally, 10-12 is reasonable.

const mongoose = require('mongoose')

const schema = new mongoose.Schema({
    username: { type: String },
    password: { 
        type: String.select:false.set(val) {
        return require('bcrypt').hashSync(val, 10)}},nickname: { type: String },
    email: { type: String },
    avatar: { type: String },
    isAdmin: { type: Boolean.default: false}})module.exports = mongoose.model('User', schema)
Copy the code

Jsonwebtoken (token value) and HTTP-Assert (simplified error return) modules need to be installed in the routes/web/index.js

    const jwt = require('jsonwebtoken')
    const assert = require('http-assert')
Copy the code

Jwt.sign () takes two parameters, an object passed in and a custom key, which I set directly in app.js

app.set('secret'.'i2u34y12oi3u4y8')
Copy the code

Assert The first parameter is a condition, the second parameter is a thrown error status code if not met, the third parameter is a text message compareSync decrypt match, the first parameter is plain text, the second parameter is ciphertext, and returns a Boolean value

    app.post('/admin/api/login'.async (req, res) => {
        const { username, password } = req.body
        // 1. Find the user according to the user name

        const user = await User.findOne({ username }).select('+password')
        assert(user, 422.'User does not exist')
        
        // 2. Verify the password
        const isValid = require('bcrypt').compareSync(password, user.password)
        assert(isValid, 422.'Password error')

        // 3. Return the token
        const token = jwt.sign({ id: user._id }, app.get('secret'))
        res.send({ userinfo: user, token: token })
    })
Copy the code

Finally, remember to apply both routes in app.js

require('./routes/admin')(app)
require('./routes/web')(app)
Copy the code

Handle cross-domain cases

Because the Express service is started on port localhost:3000 and vue-CLI created projects are started on the default port localhost:8080, cross-domain situations are definitely involved

Install the CORS module in the server directory

npm i cors
Copy the code

When applied in app.js, cross-domain is the simplest configuration

app.use(require('cors') ())Copy the code

Interface debugging

The server side in the process of writing can use Postman interface test, simple, practical, generous and beautiful, document: Postman official document

conclusion

I did encounter a lot of problems when writing node server. After all, IT was the first time I wrote it. During this period, I also seriously solved the problems encountered