開発中に下記のエラーが起きたので備忘録として記載します。
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);