PHPer+Nestjs+TypeScriptでサーバーサイド

PHPとTypeScript+Nest.jsと言う新しい組み合わせにチャレンジshてみます。
NestJS入門にあたって探したもの
MVCで書けることは前述の記事を読んでわかったのですが、長年Symfonyに甘やかされているので、単にMVCなだけだと既につらいです。
最低限で
- DI
- ORM
- テンプレートエンジン…できればTwigと同じ文法で書きたい
がほしいなと思いました。
DI
NestJSのドキュメントで一発解決しました。
https://embed.zenn.studio/card#zenn-embedded__657311005614f
普段、「フレームワークに依存しないで生のクラスでロジックを書きたい」流派に属しているので( https://speakerdeck.com/77web/sofalsekodo-huremuwakufalsewai-demodong-kimasuka )ドメインロジックのクラスに @Injectable
を書かないといけないのはぐぬぬ…という感じですが、まぁどうしても許せなければFactoryパターンを多用すればいいかと思い、これで解決としました。@Injectable
はAngularと同じような使い方でAngularに慣れていると使いやすいですね。
ORM
ActiveRecordでもギリギリ許容範囲ではあるものの、できればDoctrineと同じDataMapperがよかったので TypeScript ORM DataMapper
TypeScript Hibernate
等でググりました。
比較的すぐ見つかりました。
MikroORM https://mikro-orm.io/
もう公式サイトのトップに載ってるコード例だけで、なんとなく親近感が湧いてしまいました。(Doctrineユーザーにはわかると思う、この気持ちw)
const user = em.find(User, 1);
user.name = 'update!';
await em.flush();
テンプレートエンジン
こちらも TypeScript twig
TypeScript jinja
等とググって見つけました。
nunjucks https://mozilla.github.io/nunjucks/
サイトトップのテンプレート例が完全に違和感なかったのと、extendsとblockが使えるというので、これに決めました。
コード
まだ何もまともなロジックがない段階ですが↓に置きました。
https://embed.zenn.studio/card#zenn-embedded__c169a051edbd2
NestJSのデフォルトのテンプレートエンジンがnunjucksじゃなくてHandlebarsなので、main.tsで設定をごにょごにょ頑張ったりとか。
https://embed.zenn.studio/card#zenn-embedded__f13e58aa49778
MikroORMを使えるようにググって見つけたモジュールを設置してみたりとか。
import { Module } from '@nestjs/common';
import { MikroOrmModule } from '@mikro-orm/nestjs';
import { User } from '../../entity/user';
import * as ormConfig from '../../../config/mikro-orm.config';
@Module({
imports: [
MikroOrmModule.forRoot(ormConfig.default),
MikroOrmModule.forFeature({
entities: [User],
}),
],
exports: [MikroOrmModule],
})
export class OrmModule {}
Symfonyでやるのと同じようにDB接続情報を.envで指定できるように試行錯誤とか。
import { Logger } from '@nestjs/common';
import { Options } from '@mikro-orm/core';
import { User } from '../src/entity/user';
import * as dotenv from 'dotenv';
dotenv.config();
const logger = new Logger('MikroORM');
const config = {
entities: [User],
dbName: process.env.DATABASE_NAME,
user: process.env.DATABASE_USER,
password: process.env.DATABASE_PASSWORD,
host: process.env.DATABASE_HOST,
type: 'mysql',
port: 3306,
debug: true,
logger: logger.log.bind(logger),
} as Options;
export default config;
DBを使った機能テスト書いてみたりとか。
import { Test, TestingModule } from '@nestjs/testing';
import { HomeController } from './home.controller';
import { OrmModule } from '../module/orm/orm.module';
describe('HomeController', () => {
let homeController: HomeController;
beforeEach(async () => {
const app: TestingModule = await Test.createTestingModule({
imports: [OrmModule],
controllers: [HomeController],
providers: [],
}).compile();
homeController = app.get<HomeController>(HomeController);
});
describe('root', () => {
it('should return "Hello World!"', async () => {
expect(await homeController.index()).toEqual({ hello: 'Hello Hiromi' });
});
});
});