Node.js로 Restful CRUD API 빌드

목차


  • What CRUD API means
  • Let's Start

  • CRUD API는 무엇을 의미합니까?



    CRUD 패러다임은 CREATE , READ , UPDATEDELETE 네 가지 기본 데이터베이스 작업을 나타냅니다.

    따라서 CRUD API라는 용어는 데이터베이스에서 create , read , updatedelete 항목을 수행할 수 있는 API를 의미합니다. 이 예에서 엔터티는 직원입니다.

    시작하자



    API 엔드포인트는 다음과 같습니다.


    행동 양식
    URL
    설명


    가져 오기
    API/직원
    모든 직원 가져오기

    가져 오기
    API/직원/ID
    특정 직원 가져오기

    게시하다
    API/직원
    새 직원 만들기

    놓다
    API/직원/ID
    기존 직원 업데이트

    삭제
    API/직원/ID
    기존 직원 삭제


    저장소를 만들고 종속성을 설치합니다.
    진입점은 server.js 파일입니다.

     mkdir express-api
     cd express-api
     npm init
     npm install express helmet morgan body-parser monk joi dotenv --save 
     npm install nodemon --save-dev
    


    패키지 정보

    express: 최소한의 유연한 Node.js 웹 애플리케이션 프레임워크입니다.
    헬멧: 익스프레스 애플리케이션에서 HTTP 헤더를 보호하는 데 도움이 됩니다.
    morgan: Node.js용 HTTP 요청 로거 미들웨어입니다. js
    body-parser: 들어오는 요청 본문의 구문 분석을 담당합니다.
    몽크: MongoDB 사용에 대한 상당한 사용성 향상을 제공하는 작은 계층입니다.
    joi: 개체 스키마 설명 언어 및 개체 유효성 검사기입니다.
    dotenv: .env 파일에서 환경 변수를 로드합니다.
    nodemon: 디렉토리의 파일 변경이 감지되면 노드 응용 프로그램을 자동으로 다시 시작합니다.

    Express 웹 서버 설정
    ./src/서버.js

    const express = require('express');
    const morgan = require('morgan');
    const helmet = require('helmet');
    const bodyParser = require('body-parser');
    
    require('dotenv').config();
    
    const app = express();
    const monk = require('monk');
    
    app.use(helmet());
    app.use(morgan('dev'));
    app.use(bodyParser.json());
    
    const port = process.env.PORT || 8080;
    app.listen(port, () => {
      console.log(`Listening on port ${port}`);
    });
    


    .env 파일 생성 및 구성
    ./.env
    여기에는 우리가 사용하는 모든 환경 변수가 포함됩니다.TEST_DB_URL 변수는 데이터베이스에 테스트 데이터가 삽입되는 것을 방지하기 위한 테스트 케이스용입니다. 또한 원하는 포트 번호를 지정할 수 있습니다.

    DB_URL = localhost/my-employees
    TEST_DB_URL = localhost/test-my-employees
    PORT = 5000
    


    ./src/db/schema.js
    데이터 스키마를 생성하고 속성namejob가 따라야 하는 유효성 검사 규칙을 정의합니다.

    const Joi = require('joi');
    
    const schema = Joi.object({
        name: Joi.string()
            .min(3)
            .max(30)
            .required(),
        job: Joi.string()
            .min(3)
            .max(30)
            .required(),
    })
    
    module.exports = schema;
    


    ./src/db/connection.js
    데이터베이스에 연결

    const monk = require('monk');
    
    let dbUrl = process.env.DB_URL;
    
    if (process.env.NODE_ENV === 'test') {
      dbUrl = process.env.TEST_DB_URL;
    }
    
    const db = monk(dbUrl);
    
    module.exports = db;
    


    ./src/middlewares/index.js
    오류를 처리하고 적절한 응답을 제공하는 오류 미들웨어를 만듭니다.

    function notFound(req, res, next) {
        res.status(404);
        const error = new Error('Not Found', req.originalUrl);
        next(error);
    }
    
    function errorHandler(err, req, res, next){
        res.status(res.statusCode || 500);
        res.json({
            message: err.message,
            stack: err.stack
        });
    }
    
    module.exports = {
        notFound,
        errorHandler
    }
    

    ./src/db/connection.js , ./src/db/schema.js./src/middlewares/index.js 파일을 ./src/server.js에서 가져옵니다.

    const express = require('express');
    const morgan = require('morgan');
    const helmet = require('helmet');
    const bodyParser = require('body-parser');
    
    const { notFound, errorHandler } = require('./middlewares');
    
    require('dotenv').config();
    
    const schema = require('./db/schema');
    const db = require('./db/connection');
    const employees = db.get('employees');
    
    const app = express();
    
    app.use(helmet());
    app.use(morgan('dev'));
    app.use(bodyParser.json());
    
    app.use(notFound);
    app.use(errorHandler);
    
    const port = process.env.PORT || 8080;
    app.listen(port, () => {
      console.log(`Listening on port ${port}`);
    });
    


    이제 API 엔드포인트를 코딩합니다.

    const express = require('express');
    const morgan = require('morgan');
    const helmet = require('helmet');
    const bodyParser = require('body-parser');
    
    const { notFound, errorHandler } = require('./middlewares');
    
    require('dotenv').config();
    
    const schema = require('./db/schema');
    const db = require('./db/connection');
    const employees = db.get('employees');
    
    const app = express();
    
    app.use(helmet());
    app.use(morgan('dev'));
    app.use(bodyParser.json());
    
    /* Get all employees */
    app.get('/', async (req, res, next) => {
        try {
            const allEmployees = await employees.find({});
            res.json(allEmployees);
        } catch(error) {
            next(error);
        }
    });
    
    /* Get a specific employee */
    app.get('/:id', async (req, res, next) => {
        try {
            const { id } = req.params;
            const employee = await employees.findOne({
                _id: id
            });
    
            if(!employee) {
                const error = new Error('Employee does not exist');
                return next(error);
            }
    
        res.json(employee);
        } catch(error) {
            next(error);
        }
    });
    
    /* Create a new employee */
    app.post('/', async (req, res, next) => {
        try {
            const { name, job } = req.body;
            const result = await schema.validateAsync({ name, job });
    
            const employee = await employees.findOne({
                name,
            })
    
            // Employee already exists
            if (employee) {
                res.status(409); // conflict error
                const error = new Error('Employee already exists');
                return next(error);
            } 
    
            const newuser = await employees.insert({
                name,
                job,
            });
    
            console.log('New employee has been created');
            res.status(201).json(newuser);
        } catch(error) {
            next(error);
        }
    });
    
    /* Update a specific employee */
    app.put('/:id', async (req, res, next) => {
        try {
            const { id } = req.params;
            const { name, job } = req.body;
            const result = await schema.validateAsync({ name, job });
            const employee = await employees.findOne({
                _id: id
            });
    
            // Employee does not exist
            if(!employee) {
                return next();
            }
    
            const updatedEmployee = await employees.update({
                _id: id,
                }, {  
                $set: result},
                { upsert: true }
            );
    
            res.json(updatedEmployee);
        } catch(error) {
            next(error);
        }
    });
    
    /* Delete a specific employee */
    app.delete('/:id', async (req, res, next) => {
        try {
            const { id } = req.params;
            const employee = await employees.findOne({
                _id: id
            });
    
            // Employee does not exist
            if(!employee) {
                return next();
            }
            await employees.remove({
                _id: id
            });
    
            res.json({
                message: 'Success'
            });
    
        } catch(error) {
            next(error);
        }
    });
    
    app.use(notFound);
    app.use(errorHandler);
    
    const port = process.env.PORT || 8080;
    app.listen(port, () => {
      console.log(`Listening on port ${port}`);
    });
    

    package.json 파일로 이동하여 스크립트 섹션을 다음으로 바꿉니다.

    "scripts": {
        "start": "node src/server.js",
        "dev": "nodemon src/server.js"
      },
    


    명령npm start은 Node.js 애플리케이션을 시작하고 명령npm run dev은 Node.js 애플리케이션을 시작하지만 변경 사항이 nodemon에 의해 자동으로 모니터링된다는 유일한 차이점이 있습니다.

    우리는 ./src/server.js 파일을 "분할"하고 ./src/app.js 파일을 생성합니다.

    ./src/app.js

    const express = require('express');
    const morgan = require('morgan');
    const helmet = require('helmet');
    const bodyParser = require('body-parser');
    
    const { notFound, errorHandler } = require('./middlewares');
    
    require('dotenv').config();
    
    const schema = require('./db/schema');
    const db = require('./db/connection');
    const employees = db.get('employees');
    
    const app = express();
    
    app.use(helmet());
    app.use(morgan('dev'));
    app.use(bodyParser.json());
    
    /* Get all employees */
    app.get('/', async (req, res, next) => {
        try {
            const allEmployees = await employees.find({});
            res.json(allEmployees);
        } catch(error) {
            next(error);
        }
    });
    
    /* Get a specific employee */
    app.get('/:id', async (req, res, next) => {
        try {
            const { id } = req.params;
            const employee = await employees.findOne({
                _id: id
            });
    
            if(!employee) {
                const error = new Error('Employee does not exist');
                return next(error);
            }
    
        res.json(employee);
        } catch(error) {
            next(error);
        }
    });
    
    /* Create a new employee */
    app.post('/', async (req, res, next) => {
        try {
            const { name, job } = req.body;
            const result = await schema.validateAsync({ name, job });
    
            const employee = await employees.findOne({
                name,
            })
    
            // Employee already exists
            if (employee) {
                res.status(409); // conflict error
                const error = new Error('Employee already exists');
                return next(error);
            } 
    
            const newuser = await employees.insert({
                name,
                job,
            });
    
            console.log('New employee has been created');
            res.status(201).json(newuser);
        } catch(error) {
            next(error);
        }
    });
    
    /* Update a specific employee */
    app.put('/:id', async (req, res, next) => {
        try {
            const { id } = req.params;
            const { name, job } = req.body;
            const result = await schema.validateAsync({ name, job });
            const employee = await employees.findOne({
                _id: id
            });
    
            // Employee does not exist
            if(!employee) {
                return next();
            }
    
            const updatedEmployee = await employees.update({
                _id: id,
                }, {  
                $set: result},
                { upsert: true }
            );
    
            res.json(updatedEmployee);
        } catch(error) {
            next(error);
        }
    });
    
    /* Delete a specific employee */
    app.delete('/:id', async (req, res, next) => {
        try {
            const { id } = req.params;
            const employee = await employees.findOne({
                _id: id
            });
    
            // Employee does not exist
            if(!employee) {
                return next();
            }
            await employees.remove({
                _id: id
            });
    
            res.json({
                message: 'Success'
            });
    
        } catch(error) {
            next(error);
        }
    });
    
    app.use(notFound);
    app.use(errorHandler);
    
    module.exports = app;
    


    ./src/서버.js

    const app = require('./app');
    
    const port = process.env.PORT || 8080;
    app.listen(port, () => {
      console.log(`Listening on port ${port}`);
    });
    


    마지막 단계는 코드를 리팩토링하고 ./src/routes/employees 를 생성하는 것입니다.

    ./src/routes/employees.js

    const express = require('express');
    const schema = require('../db/schema');
    const db = require('../db/connection');
    
    const employees = db.get('employees');
    
    const router = express.Router();
    
    /* Get all employees */
    router.get('/', async (req, res, next) => {
      try {
        const allEmployees = await employees.find({});
        res.json(allEmployees);
      } catch (error) {
        next(error);
      }
    });
    
    /* Get a specific employee */
    router.get('/:id', async (req, res, next) => {
      try {
        const { id } = req.params;
        const employee = await employees.findOne({
          _id: id,
        });
    
        if (!employee) {
          const error = new Error('Employee does not exist');
          return next(error);
        }
    
        res.json(employee);
      } catch (error) {
        next(error);
      }
    });
    
    /* Create a new employee */
    router.post('/', async (req, res, next) => {
      try {
        const { name, job } = req.body;
        const result = await schema.validateAsync({ name, job });
    
        const employee = await employees.findOne({
          name,
        });
    
        // Employee already exists
        if (employee) {
          const error = new Error('Employee already exists');
          res.status(409); // conflict error
          return next(error);
        }
    
        const newuser = await employees.insert({
            name,
            job,
        });
    
        res.status(201).json(newuser);
      } catch (error) {
        next(error);
      }
    });
    
    /* Update a specific employee */
    router.put('/:id', async (req, res, next) => {
      try {
        const { id } = req.params;
        const { name, job } = req.body;
        const result = await schema.validateAsync({ name, job });
        const employee = await employees.findOne({
          _id: id,
        });
    
        // Employee does not exist
        if (!employee) {
          return next();
        }
    
        const updatedEmployee = await employees.update({
          _id: id,
        }, { $set: result },
        { upsert: true });
    
        res.json(updatedEmployee);
      } catch (error) {
        next(error);
      }
    });
    
    /* Delete a specific employee */
    router.delete('/:id', async (req, res, next) => {
      try {
        const { id } = req.params;
        const employee = await employees.findOne({
          _id: id,
        });
    
        // Employee does not exist
        if (!employee) {
          return next();
        }
        await employees.remove({
          _id: id,
        });
    
        res.json({
          message: 'Employee has been deleted',
        });
      } catch (error) {
        next(error);
      }
    });
    
    module.exports = router;
    


    ./src/app.js 파일은 다음과 같습니다.

    const express = require('express');
    const morgan = require('morgan');
    const helmet = require('helmet');
    const bodyParser = require('body-parser');
    
    const { notFound, errorHandler } = require('./middlewares');
    
    const app = express();
    
    require('dotenv').config();
    
    app.use(helmet());
    app.use(morgan('dev'));
    app.use(bodyParser.json());
    
    const employees = require('./routes/employees');
    
    app.use('/api/employees', employees);
    
    app.use(notFound);
    app.use(errorHandler);
    
    module.exports = app;
    
    


    내 github 저장소express-api에서 전체 프로젝트를 확인할 수 있습니다.

    좋은 웹페이지 즐겨찾기