Typescript로 익스프레스 설정 - 데이터베이스 추가 서비스 쿼리

소개



이 시리즈에서는 Typescript를 사용하여 익스프레스 서버를 설정하고 PostgresSql 데이터베이스를 쿼리하기 위한 ORM으로 TypeOrm를 사용하고 테스트를 위해 JestSuperTest도 사용합니다. 이 시리즈의 목표는 본격적인 노드 백엔드를 만드는 것이 아니라 Express 및 TypeScript를 사용하여 노드 백엔드를 개발하려는 경우 시작점으로 사용할 수 있는 TypeScript를 사용하여 Express 시작 프로젝트를 설정하는 것입니다.

개요



이 시리즈는 nodejs , express , typescripttypeorm 작업에 어느 정도 익숙하고 경험이 있는 초보자에게 권장되지 않습니다. 시리즈의 4부인 이 게시물에서는 다음을 수행합니다.
  • Todo 엔터티를 사용하여 데이터베이스를 쿼리합니다.
  • Todo 서비스를 생성합니다.
  • 당사의 Todo 컨트롤러에서 Todo 서비스를 사용하십시오.

  • 1단계: 데이터베이스 쿼리



    우리의 todo.controller.ts에서 이제 하드 코딩된 todos를 제거하고 다음과 같이 데이터베이스를 쿼리합니다.

    import { Request, Response } from 'express';
    
    import { AppDataSource } from '../../datasource';
    import { Todos } from './todos.entity';
    
    class TodoController {
      async getAllTodos(req: Request, res: Response) {
        try {
          const todos = await AppDataSource.getRepository(Todos)
            .createQueryBuilder('todos')
            .getMany();
          return res.status(200).json({
            status: true,
            statusCode: 200,
            todos,
          });
        } catch (error) {
          return res.status(500).json({
            status: false,
            statusCode: 500,
            message: 'Something unusual Happened',
          });
        }
      }
    
      async getTodoById(req: Request, res: Response) {
        try {
          const { id } = req.params;
          const todo = await AppDataSource.getRepository(Todos)
            .createQueryBuilder('todos')
            .where('todos.id = :id', { id })
            .getOne();
          if (!todo) {
            return res.status(404).json({
              status: false,
              statusCode: 404,
              message: `todo not found for id - ${id}`,
            });
          }
    
          return res.status(200).json({
            status: true,
            statusCode: 200,
            todo,
          });
        } catch (error) {
          return res.status(500).json({
            status: false,
            statusCode: 500,
            message: 'Something unusual Happened',
          });
        }
      }
    }
    
    export const todosController = new TodoController();
    


    데이터베이스를 쿼리하기 위해 queryBuilder 패턴을 사용합니다. createQueryBuilder 함수는 테이블 이름을 가져오므로 올바른 이름을 전달해야 합니다. try-catch 블록은 코드나 orm 라이브러리 등에 의해 발생하는 오류를 처리하는 데 필요합니다. 앞으로 나올 튜토리얼에서는 이 모든 것을 처리할 비동기 처리기 미들웨어를 만들 것입니다. 이 모든 try-catch 블록을 제거합니다.

    2단계: Todo 서비스 설정



    모든 쿼리와 데이터베이스 관련 로직을 컨트롤러에 작성하는 대신 별도의 서비스 메서드에 작성하는 것이 모범 사례 중 하나입니다.
  • 하나의 작업(코드의 원자 단위)을 수행하는 코드를 작성하면 장기적으로 쉽게 테스트하고 읽을 수 있으며 확장할 수 있습니다.
  • 프로젝트 전반에 걸쳐 많은 컨트롤러, 기타 서비스에서 사용할 수 있는 재사용 가능한 기능이 있습니다.
  • 코드를 테스트할 때 요청 핸들러를 모방하는 대신 서비스를 쉽게 모방할 수 있습니다. 서비스 기능을 단위 테스트할 수도 있습니다.

  • 각 컨트롤러 코드에서 getRepository 함수를 호출해야 한다는 것을 알 수 있습니다. 이 코드 조각을 엔티티 파일 자체 또는 컨트롤러 클래스 외부로 이동할 수 있습니다. 그러나 우리가 Typescript를 사용하고 있다는 사실을 감안할 때 BaseService 클래스를 만들 수 있습니다. src/utils에서 새 파일BaseService.ts을 만들고 다음을 붙여넣습니다.

    import { EntityTarget, Repository } from 'typeorm';
    
    import { AppDataSource } from '../datasource';
    
    export class BaseService<T> {
      repository: Repository<T>;
    
      constructor(entity: EntityTarget<T>) {
        this.repository = AppDataSource.getRepository(entity);
      }
    }
    

    BaseService는 일반 클래스이며 유일한 작업은 리포지토리를 생성하여 이를 확장하는 모든 서비스 클래스가 this.respository 속성을 갖도록 하는 것입니다. 프로젝트가 커짐에 따라 이 기본 클래스에서 많은 반복 코드를 이동할 수 있습니다. 이제 todos.service.ts 내부에 src/api/todos.service.ts를 생성해 보겠습니다.

    import { BaseService } from '../../utils/BaseService';
    
    import { Todos, TodoStatus } from './todos.entity';
    
    class TodosService extends BaseService<Todos> {
      constructor() {
        super(Todos);
      }
    
      getAllTodos() {
        return this.repository.createQueryBuilder('todos').getMany();
      }
    
      getTodoById(todoId: string) {
        return this.repository
          .createQueryBuilder('todos')
          .where('todos.id = :id', { id: todoId })
          .getOne();
      }
    
      createTodo(text: string, status: TodoStatus) {
        return this.repository
          .createQueryBuilder()
          .insert()
          .into(Todos)
          .values({
            text,
            status,
          })
          .returning('*')
          .execute();
      }
    }
    
    export const todosService = new TodosService();
    


    생성자에서 super(Todo)를 호출하면 BaseService 클래스 생성자가 호출되고 엔터티가 여기에 전달됩니다. 또한 서비스에 새로운 기능createTodo을 추가했습니다.

    3단계: 컨트롤러에서 서비스 사용



    이제 컨트롤러 파일에서 서비스 메서드를 사용합니다.

    import { Request, Response } from 'express';
    
    import { todosService } from './todos.service';
    
    class TodoController {
      async getAllTodos(req: Request, res: Response) {
        try {
          const todos = await todosService.getAllTodos();
          return res.status(200).json({
            status: true,
            statusCode: 200,
            todos,
          });
        } catch (error) {
          return res.status(500).json({
            status: false,
            statusCode: 500,
            message: 'Something unusual Happened',
          });
        }
      }
    
      async getTodoById(req: Request, res: Response) {
        try {
          const { id: todoId } = req.params;
          const todo = await todosService.getTodoById(todoId);
          if (!todo) {
            return res.status(404).json({
              status: false,
              statusCode: 404,
              message: `todo not found for id - ${todoId}`,
            });
          }
    
          return res.status(200).json({
            status: true,
            statusCode: 200,
            todo,
          });
        } catch (error) {
          return res.status(500).json({
            status: false,
            statusCode: 500,
            message: 'Something unusual Happened',
          });
        }
      }
    
      async createTodo(req: Request, res: Response) {
        try {
          const { text, status } = req.body;
          const newTodo = await todosService.createTodo(text, status);
          return res.status(201).json({
            status: true,
            statusCode: 201,
            todo: newTodo.raw,
          });
        } catch (error) {
          return res.status(500).json({
            status: false,
            statusCode: 500,
            message: 'Something unusual Happened',
          });
        }
      }
    }
    
    export const todosController = new TodoController();
    

    todos.router.ts에 엔드포인트를 등록해 보겠습니다.

     addRoutes(): void {
        this.router.get('/', todosController.getAllTodos);
        this.router.post('/', todosController.createTodo);
        this.router.get('/:id', todosController.getTodoById);
     }
    


    이제 npm run dev를 실행하여 웹 서버를 시작하고 끝점을 테스트할 수 있습니다. 나는 개인적으로 우편 배달부를 사용하지만 이러한 소규모 프로젝트의 경우 vscode에 REST 클라이언트를 사용하고 여기에 내 api.http가 있습니다.

    GET http://localhost:8080/api/todos
    
    ###
    GET http://localhost:8080/api/todos/:todoId
    
    ###
    POST http://localhost:8080/api/todos 
    Content-Type: application/json
    
    {
      "text": "Walk for 30 minutes",
      "status": "pending"
    }
    


    요약



    그럼 서비스 설정을 마쳤고 컨트롤러에서도 사용했습니다. 이 자습서의 모든 코드는 feat/querying-database 분기 here 아래에서 찾을 수 있습니다. 다음 튜토리얼에서는 전역 Error 핸들러와 async 미들웨어를 다음 시간 PEACE까지 설정하여 시도하고 반복되는 코드를 모두 제거할 것입니다.

    좋은 웹페이지 즐겨찾기