NestJS에서 TypeORM을 사용해보았으나 Sequelize가 낫다고 판단했다. TypeORM에서 복잡한 쿼리는 쿼리 빌더(query builder)를 사용해야 하는 불편함이 있었고, Sequelize에 비해 쿼리 최적화를 잘 못해준다는 문제가 있었다.
NestJS에서 Sequelize를 사용하기 위해서 sequelize-typescript 패키지를 설치한다.
// app.module.ts
@Module({
imports: [
SequelizeModule.forRoot({
dialect: 'postgres',
name: 'now-db',
host: env.NOW_DB_HOST,
port: env.NOW_DB_PORT || 5432,
username: env.NOW_DB_USERNAME,
password: env.NOW_DB_PASSWORD,
database: env.NOW_DB_DATABASE,
models: [User], // <- 사용할 모델 등록
autoLoadModels: false,
}),
],
controllers: [],
providers: [],
})
export class AppModule {}
// user.model.ts
@Table({ tableName: 'users' })
export class User extends Model<User> {
@Column(DataType.STRING(100))
name: string
@Column(DataType.STRING(30))
password: string
@AllowNull
@Column(DataType.STRING(30))
email?: string | null
}
// user.service.ts
@Injectable()
export class UserService {
constructor(
@InjectModel(User)
private readonly userModel: typeof User,
) {}
async findUsers() {
return this.userModel.findAll();
}
}
만약 now-db가 아닌 next-db라는 데이터베이스를 추가하고 싶다면 아래와 같이 수정한다.
// app.module.ts
@Module({
imports: [
SequelizeModule.forRoot({
dialect: 'postgres',
name: 'now-db',
host: env.NOW_DB_HOST,
port: env.NOW_DB_PORT || 5432,
username: env.NOW_DB_USERNAME,
password: env.NOW_DB_PASSWORD,
database: env.NOW_DB_DATABASE,
models: [User],
autoLoadModels: false,
}),
SequelizeModule.forRoot({ // <- next-db 추가
dialect: 'postgres',
name: 'next-db',
host: env.NEXT_DB_HOST,
port: env.NEXT_DB_PORT || 5432,
username: env.NEXT_DB_USERNAME,
password: env.NEXT_DB_PASSWORD,
database: env.NEXT_DB_DATABASE,
models: [Item],
autoLoadModels: false,
}),
],
controllers: [],
providers: [],
})
export class AppModule {}
// item.model.ts
@Table({ tableName: 'items' })
export class Item extends Model<Item> {
@Column(DataType.STRING(300))
name: string
@Column(DataType.STRING(300))
code: string
@AllowNull
@Column(DataType.DECIMAL)
price?: string | null
}
// user.service.ts
@Injectable()
export class UserService {
constructor(
@InjectModel(User, 'now-db') // <- DB명 추가
private readonly userModel: typeof User,
) {}
async findUsers() {
return this.userModel.findAll();
}
}
// item.service.ts
@Injectable()
export class ItemService {
constructor(
@InjectModel(Item, 'next-db') // <- DB명 추가
private readonly itemModel: typeof Item,
@InjectConnection('next-db') // <- Raw 쿼리 사용하고 싶을 경우
private readonly sequelizeConnection: Sequelize,
) {}
async findItems() {
return this.sequelizeConnection.query(`SELECT id, name, code, price FROM items`, {
type: QueryTypes.SELECT,
})
}
}
TypeORM의 entity처럼 sequelize-typescript를 사용하기 위해서는 model을 작성해줘야 한다. 기존에 생성된 데이터베이스의 스키마를 모델로 작성하는 일이 여간 귀찮은 일이 아니기에 간단한 스니펫을 만들어 사용하면 편하다.
import { DialectPostgres, IConfig, ModelBuilder } from 'sequelize-typescript-generator'
(async () => {
const schema = 'common' // <- postgres 스키마 사용 시 추가
const config: IConfig = {
connection: {
dialect: 'postgres',
host: 'localhost',
database: 'test',
username: 'user1',
password: 'testpw',
},
metadata: {
indices: false,
case: 'CAMEL',
schema,
tables: ['table1', 'table2'], // <- 테이블명 변경
},
output: {
clean: true,
outDir: './src/model', // <- 저장하고 싶은 경로
},
strict: true,
}
const dialect = new DialectPostgres()
const builder = new ModelBuilder(config, dialect)
try {
await builder.build()
}
catch (err) {
console.error(err)
process.exit(1)
}
})()
위 코드를 model-generator.ts 라는 파일로 저장 후 아래 명령어로 실행한다.
ts-node model-generator.ts
나오는 결과물을 입맛에 맞게 수정해서 사용해도 되고, 직접 모델을 작성해줘도 된다.