для эффективной обработки ошибок необходимо использовать промежуточное ПО

создайте middleware/error.js и добавьте

const errorHandler = (err, req, res, next) => {
res.status(500).json({
sucess:false,
error:err.message
});
}
module.exports = errorHandler;

нам просто нужно сделать 2 настройки в server.js, чтобы включить файл и использовать его

const express = require('express');
const dotenv = require('dotenv');
const morgan = require('morgan');
const connectDB = require('./config/db');
const errorHandler = require('./middleware/error');
// Load env vars
dotenv.config({ path: './config/config.env' });
//connect to db
connectDB();
// Route files
const bootcamps = require('./routes/bootcamps');
const app = express();
//Body parser
app.use(express.json());
// dev logging middleware
if (process.env.NODE_ENV == 'development') {
  app.use(morgan('dev'));
}
// Mount routers
app.use('/api/v1/bootcamps', bootcamps);
app.use(errorHandler);
const PORT = process.env.PORT || 5000;
const server = app.listen(
  PORT,
  console.log(`server running in ${process.env.NODE_ENV} mode on PORT ${PORT}`)
);
//handle unhandled promise rejection
process.on('unhandledRejection', (err, promise) => {
    console.log(`Error: ${err.message}`);
    //console.log('dsds');
    // Close server & exit process
    //server.close(() => process.exit(1));
  });
//mongodb://localhost:27017/dev-learn

обновите controllers/bootcamps.js и добавьте

exports.getBootcamp = async (req,res,next) => {
    try {
        const bootcamp = await Bootcamp.findById(req.params.id);
        if(!bootcamp){
            return res.status(400).json({sucess:false});
        }
        res.status(200).json({success:true,data:bootcamp});
} catch (err) {
        //res.status(400).json({sucess:false});
        next(err);
    }
};

вот как у нас есть середина для отображения сообщений об ошибках

теперь мы можем больше настраивать наше сообщение, используя наследование и изменяя содержимое нашего сообщения.

создайте utils/errorResponse.js и добавьте

class ErrorResponse extends Error {
constructor(message, statusCode) {
super(message);
this.statusCode = statusCode;
//Error.captureStackTrace(this, this.constructor);
}
}
module.exports = ErrorResponse;

обновить middleware/error.js с помощью

const errorHandler = (err, req, res, next) => {
  res.status(err.statusCode || 500).json({
    sucess:false,
    error:err.message || 'Server error'
  });
};
module.exports = errorHandler;

и controllers/bootcamps.jsмодуль getbootcamp с

exports.getBootcamp = async (req,res,next) => {
    try {
        const bootcamp = await Bootcamp.findById(req.params.id);
        if(!bootcamp){
            return next(new ErrorResponse(`Bootcamp not found with id of ${req.params.id}`,400));
        }
        res.status(200).json({success:true,data:bootcamp});
} catch (err) {
        //res.status(400).json({sucess:false});
        next(new ErrorResponse(`Bootcamp not found with id of ${req.params.id}`,400));
    }
};

мы можем добавить больше настроек, поэтому обновите middleware/error.js с помощью

const errorHandler = (err, req, res, next) => {
let error = { ...err };
error.message = err.message;
//Mongoose bad ObjectId
  if (err.name === 'CastError') {
    const message = `Resource not found ${err.value}`;
    error = new ErrorResponse(message, 404);
  }
//   // Mongoose duplicate key
  if (err.code === 11000) {
    const message = 'Duplicate field value entered';
    error = new ErrorResponse(message, 400);
  }
  //   // Mongoose validation error
  if (err.name === 'ValidationError') {
    const message = Object.values(err.errors).map(val => val.message);
    error = new ErrorResponse(message, 400);
  }
res.status(error.statusCode || 500).json({
    sucess:false,
    error: error.message || 'Server error'
  });
};

и все вместе с controllers/bootcamps.js

const Bootcamp = require('../models/Bootcamp');
const ErrorResponse = require('../utils/errorResponse');
exports.getBootcamps = async (req,res,next) => {
    try {
        const bootcamps = await Bootcamp.find();
        res.status(200).json({success:true,count:bootcamps.length,data:bootcamps});
        //next(err);
} catch (err) {
        next(err);
    }
};
exports.getBootcamp = async (req,res,next) => {
    try {
        const bootcamp = await Bootcamp.findById(req.params.id);
        if(!bootcamp){
            return next(new ErrorResponse(`Bootcamp not found with id of ${req.params.id}`,400));
        }
        res.status(200).json({success:true,data:bootcamp});
} catch (err) {
        //res.status(400).json({sucess:false});
        next(err);
    }
};
exports.createBootcamp = async (req,res,next) => {
    try{
        const bootcamp = await Bootcamp.create(req.body);
        res.status(201).json({
            success:true,
            data:bootcamp
        });
    } catch (err){
        next(err);
    }
};
exports.updateBootcamp = async (req,res,next) => {
    try{
        const bootcamp = await Bootcamp.findByIdAndUpdate(req.params.id,req.body,{
            new:true,
            runValidators:true
        });
        if(!bootcamp){
            return next(new ErrorResponse(`Bootcamp not found with id of ${req.params.id}`,400));
        }
        res.status(200).json({sucess:true,data:bootcamp});
    } catch(err){
        next(err);
    }
};
exports.deleteBootcamp = async (req,res,next) => {
    try{
        const bootcamp = await Bootcamp.findByIdAndDelete(req.params.id);
        if(!bootcamp){
            return next(new ErrorResponse(`Bootcamp not found with id of ${req.params.id}`,400));
        }
        res.status(200).json({sucess:true,data:{}});
    } catch(err){
        next(err);
    }
};

СУХОЙ (Не повторяйтесь)

так что в наших контроллерах/bootcamps.js у нас везде один и тот же блок catch, можем ли мы что-то с этим сделать? да

создайте новый файл в middleware/async.js и добавьте

const asyncHandler = fn => (req, res, next) =>
Promise.resolve(fn(req, res, next)).catch(next);
module.exports = asyncHandler;

а затем controller/bootcamps.js замените код на

const Bootcamp = require('../models/Bootcamp');
const asyncHandler = require('../middleware/async');
const ErrorResponse = require('../utils/errorResponse');
exports.getBootcamps = asyncHandler(async (req, res, next) => {
        const bootcamps = await Bootcamp.find();
        res.status(200).json({success:true,count:bootcamps.length,data:bootcamps});
        //next(err);
});
exports.getBootcamp = asyncHandler(async (req,res,next) => {
        const bootcamp = await Bootcamp.findById(req.params.id);
        if(!bootcamp){
            return next(new ErrorResponse(`Bootcamp not found with id of ${req.params.id}`,400));
        }
        res.status(200).json({success:true,data:bootcamp});
});
exports.createBootcamp = asyncHandler(async (req,res,next) => {
        const bootcamp = await Bootcamp.create(req.body);
        res.status(201).json({
            success:true,
            data:bootcamp
        });
});
exports.updateBootcamp = asyncHandler(async (req,res,next) => {
        const bootcamp = await Bootcamp.findByIdAndUpdate(req.params.id,req.body,{
            new:true,
            runValidators:true
        });
        if(!bootcamp){
            return next(new ErrorResponse(`Bootcamp not found with id of ${req.params.id}`,400));
        }
        res.status(200).json({sucess:true,data:bootcamp});
    
});
exports.deleteBootcamp = asyncHandler(async (req,res,next) => {
        const bootcamp = await Bootcamp.findByIdAndDelete(req.params.id);
        if(!bootcamp){
            return next(new ErrorResponse(`Bootcamp not found with id of ${req.params.id}`,400));
        }
        res.status(200).json({sucess:true,data:{}});
    
});

Слагификация мангуста

npm i slugify

добавление слагов к нашему заголовку необходимо для улучшения SEO

перейдите в models/Bootcamp.js и добавьте это выше

const slugify = require('slugify');

а это внизу

// Create bootcamp slug from the name
BootcampSchema.pre('save', function(next) {
this.slug = slugify(this.name, { lower: true });
next();
});

Местоположение MapQuest API GeoJSON

в основном используется для указания полей местоположения, которые мы добавили в нашу модель

создайте свою учетную запись на https://developer.mapquest.com/

npm i node-geocoder

получить ключи от

https://developer.mapquest.com/user/me/apps

в config/config.env

GEOCODER_PROVIDER=mapquest
GEOCODER_API_KEY=yourconsumerkey

создайте utils/geocoder.js и добавьте

const NodeGeocoder = require('node-geocoder');
const options = {
  provider: process.env.GEOCODER_PROVIDER,
  httpAdapter: 'https',
  apiKey: process.env.GEOCODER_API_KEY,
  formatter: null
};
const geocoder = NodeGeocoder(options);
module.exports = geocoder;

и в models/Bootcamp.js

добавьте это в строку выше

const geocoder = require('../utils/geocoder');

и это в строке ниже

// Geocode & create location field
BootcampSchema.pre('save', async function(next) {
  const loc = await geocoder.geocode(this.address);
  this.location = {
    type: 'Point',
    coordinates: [loc[0].longitude, loc[0].latitude],
    formattedAddress: loc[0].formattedAddress,
    street: loc[0].streetName,
    city: loc[0].city,
    state: loc[0].stateCode,
    zipcode: loc[0].zipcode,
    country: loc[0].countryCode
  };
// Do not save address in DB
  this.address = undefined;
  next();
});

и в следующий раз, когда вы сделаете любой запрос на создание, также будет поле местоположения

создать новый буткемп с этими данными

{
"name": "ModernTech Bootcamp",
    "description": "ModernTech has one goal, and that is to make you a rockstar developer and/or designer with a six figure salary. We teach both development and UI/UX",
    "website": "https://moderntech.com",
    "phone": "(222) 222-2222",
    "email": "[email protected]",
    "address": "220 Pawtucket St, Lowell, MA 01854",
    "careers": ["Web Development", "UI/UX", "Mobile Development"],
    "housing": false,
    "jobAssistance": true,
    "jobGuarantee": false,
    "acceptGi": true
}

Это все на данный момент