0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Express.js×TypeScriptでDI用のContainerクラスを作る

0
Posted at

開発中に下記のエラーが起きたので備忘録として記載します。

AuthController.ts
import { Request, Response } from 'express'; 
import { AuthService } from '../../application/services/AuthService'; 
export class AuthController { 
constructor( private AuthService:AuthService ){} 
register = async ( req: Request, res: Response )=>{ const {email,password} = req.body; 
const user = await this.AuthService.register( email,password ); res.json(user); } }
Router.ts
import { Router } from "express"; 
import { AuthController } from "../../presentation/controllers/AuthController"; 
const router = Router(); 
router.post("/register", AuthController.register); router.post("/login", controller.login); router.post("/logout", controller.logout); 
export default router;

(エラー)

Property 'register' does not exist on type 'typeof AuthController'.

エラーの原因は、register が インスタンスメソッド なのに、AuthController.register と クラス名から直接呼び出そうとしているためです。

現在のAuthControllerはこうなっています。

AuthController.ts
export class AuthController {
    constructor(
        private AuthService: AuthService
    ) {}

    register = async (req: Request, res: Response) => {
        ...
    }
}

register staticではないので、

AuthController.ts
AuthController.register

ではアクセスできません。

解決方法: DI(依存性注入)用のファイルを作る

実際のプロジェクトでは Router 内で生成せず、別ファイルで組み立てることが多いです。

src/
├── domain/
│   └── ...
├── application/
│   └── ...
├── infrastructure/
│   └── ...
├── presentation/
│   ├── controllers/
│   └── routes/
│
├── container/
│   └── authContainer.ts
│
└── server.ts

Controller

src/presentation/controllers/AuthController.ts
import { Request, Response } from 'express';
import { AuthService }  from '../../application/services/AuthService';

type Test = import("express-session").SessionData;

const test: Test = {
  cookie: {} as any,
  userId: 1
};

export class AuthController {
    constructor(
        private AuthService:AuthService
    ){}

    register = async (
        req: Request,
        res: Response
    )=>{
        const {email,password} = req.body;
        const user = await this.AuthService.register(
            email,password
        );
        res.json(user);
    }

    login = async (
        req: Request,
        res: Response
    ) =>{
        const {email,password}=req.body;
        const user = await this.AuthService.login(email,password);
        
        if(!user){
            return res.status(401).json({
                message: "login failed"
            });
        }
        

        req.session.userId = user.id;// req.session.userId = user.id;
        res.json({
            message: "login success"
        });
    }

    logout = (
        req: Request,
        res: Response
    ) =>{
        req.session.destroy(()=>{
            res.json({message:"logout"})
        });
    }
}

src/presentation/controllers/TodoController.ts
import { Request, Response } from "express";
import { TodoService } from '../../application/services/TodoService';

export class TodoController {
    constructor(
        private TodoService:TodoService
    ){}

    getAll = async (
        req: Request,
        res: Response
    ) => {

        if(!req.session.userId){
            return res.json(null);
        }
        const todos = await this.TodoService.getTodos(
            req.session.userId
        );
        res.json(todos);
    }

    create = async (
        req: Request,
        res: Response
    ) => {
        
        if(!req.session.userId){
            return res.json(null);
        }

        const todo = await this.TodoService.createTodo(
            req.body.title,
            req.session.userId
        );
        res.json(todo);
    }
    update = async (
        req:Request,
        res:Response
    ) => {
        await this.TodoService.updateTodo(
        Number(req.params.id),
        req.body.title,
        req.body.completed
        );

        res.json({message:"updated"});
    };

    delete = async (
        req:Request,
        res:Response
    ) => {

        await this.TodoService.deleteTodo(
        Number(req.params.id)
        );

        res.json({message:"deleted"});
    };
}


Repositoryクラスの作成

UserRepository.ts
import { pool } from '../db/postgres';
import { IUserRepository } from '../../domain/repositories/IUserRepository';
import { User } from '../../domain/entities/User';

export class UserRepository implements IUserRepository {
    async findByEmail(email: string): Promise<User | null> {
        const result = await pool.query(
            `SELECT * FROM users WHERE email = $1`,
            [email]
        );
        return result.rows[0] || null;
    }

    async create(email: string, password: string): Promise<User> {
        const result = await pool.query(
            `
            INSERT INTO users(email,password)
            VALUES($1,$2)
            RETURNING *
            `,
            [email,password]
        );
        return result.rows[0];
    }
}

TodoRepository.ts
import { pool } from '../db/postgres';
export class TodoRepository {
    async findAll(userId:number){
        const result = await pool.query(
            `
            SELECT *
            FROM todos
            WHERE user_id=$1
            ORDER BY id
            `
            ,
            [userId]
        );
        return result.rows;
    }

    async create(title:string,userId:number){
        const result = await pool.query(
            `
            INSERT INTO todos(title,user_id)
            VALUES($1,$2)
            RETURNING *
            `
            ,
            [title,userId]
        );
        return result.rows[0];
    }

    async update(
        id:number,
        title:string,
        completed:boolean
    ) {

        await pool.query(
        `
        UPDATE todos
        SET title=$1,
            completed=$2
        WHERE id=$3
        `,
        [title,completed,id]
        );
    }
    async delete(id:number) {
        await pool.query(
        `
        DELETE FROM todos
        WHERE id=$1
        `,
        [id]
        );
    }
}


Serviceクラス

src/application/services/AuthService.ts
import bcrypt from 'bcrypt';
import { UserRepository } from '../../infrastructure/repositories/UserRepository';

export class AuthService {
    constructor(
        private userRepo: UserRepository
    ){}

    async register(
        email: string,
        password: string
    ){
        const hash = await bcrypt.hash(password,10);
        return await this.userRepo.create(
            email,hash
        );
    }

    async login(email:string,password:string){
        const user = await this.userRepo.findByEmail(email);
        if(!user) return null;
        const ok = await bcrypt.compare(
            password,
            user.password
        );
        return ok ? user : null;
    }
}


src/application/services/TodoService.ts
import { TodoRepository } from '../../infrastructure/repositories/TodoRepository';

export class TodoService {
    constructor(
        private repo: TodoRepository
    ){}

    getTodos(userId:number){
        return this.repo.findAll(userId);
    }

    createTodo(title: string, userId:number){
        return this.repo.create(title, userId);
    }

    updateTodo(
        id: number,
        title:string,
        completed:boolean
    ){
        return this.repo.update(
            id,title,completed
        );
    }
    deleteTodo(id:number){
        return this.repo.delete(id);
    }
}

Conatinerクラスの作成

src/container/todoContainer.ts
import { TodoController } from "../presentation/controllers/TodoController";
import { TodoRepository } from "../infrastructure/repositories/TodoRepository";
import { TodoService } from "../application/services/TodoService";

const todoRepo = new TodoRepository();
const todoService = new TodoService(todoRepo);
export const todoController = new TodoController(todoService);
authContainer.ts
import { AuthController } from "../presentation/controllers/AuthController";
import { UserRepository } from "../infrastructure/repositories/UserRepository";
import { AuthService } from "../application/services/AuthService";

const userRepo = new UserRepository();
const authService = new AuthService(userRepo);
export const authController = new AuthController(authService);
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?