Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
NestJS a progressive web framework
Search
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
jiko21
February 12, 2020
Technology
3
2.2k
NestJS a progressive web framework
関西Node学園#9での発表資料です
GitHub:
https://github.com/jiko21/nest-todo-rest
jiko21
February 12, 2020
Tweet
Share
More Decks by jiko21
See All by jiko21
型情報を手繰り寄せる技術~TypeScript Compiler APIによる型解析実践~
jiko21
0
1.1k
Creating a Next.js-style Framework with Bun and Hono
jiko21
0
170
Array Grouping will soon be arriving at TypeScript
jiko21
0
140
Copying Array Methods arrived at TypeScript
jiko21
1
720
SSRで動的に OGP画像を生成したい! 〜Cloudflare Workersから@vercel/og移行編〜
jiko21
0
150
node:test will replace Jest?
jiko21
0
100
どこでも動かすために… TypeScriptでライブラリ開発の すゝめ
jiko21
2
410
レガシーなフロントエンドをリプレイスする
jiko21
5
1.6k
Deep Dive Into Vue Composition API
jiko21
0
3.3k
Other Decks in Technology
See All in Technology
仕様書駆動AI開発の実践: Issue→Skill→PRテンプレで 再現性を作る
knishioka
2
590
制約が導く迷わない設計 〜 信頼性と運用性を両立するマイナンバー管理システムの実践 〜
bwkw
3
870
M&A 後の統合をどう進めるか ─ ナレッジワーク × Poetics が実践した組織とシステムの融合
kworkdev
PRO
1
410
Contract One Engineering Unit 紹介資料
sansan33
PRO
0
13k
フルカイテン株式会社 エンジニア向け採用資料
fullkaiten
0
10k
日本語テキストと音楽の対照学習の技術とその応用
lycorptech_jp
PRO
1
430
Context Engineeringが企業で不可欠になる理由
hirosatogamo
PRO
3
410
予期せぬコストの急増を障害のように扱う――「コスト版ポストモーテム」の導入とその後の改善
muziyoshiz
1
1.6k
【5分でわかる】セーフィー エンジニア向け会社紹介
safie_recruit
0
42k
CDK対応したAWS DevOps Agentを試そう_20260201
masakiokuda
1
200
プロポーザルに込める段取り八分
shoheimitani
1
170
コスト削減から「セキュリティと利便性」を担うプラットフォームへ
sansantech
PRO
3
1.3k
Featured
See All Featured
Speed Design
sergeychernyshev
33
1.5k
Building a Modern Day E-commerce SEO Strategy
aleyda
45
8.6k
The Straight Up "How To Draw Better" Workshop
denniskardys
239
140k
Reflections from 52 weeks, 52 projects
jeffersonlam
356
21k
Have SEOs Ruined the Internet? - User Awareness of SEO in 2025
akashhashmi
0
270
Unlocking the hidden potential of vector embeddings in international SEO
frankvandijk
0
170
How People are Using Generative and Agentic AI to Supercharge Their Products, Projects, Services and Value Streams Today
helenjbeal
1
120
How to Ace a Technical Interview
jacobian
281
24k
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
254
22k
Agile Actions for Facilitating Distributed Teams - ADO2019
mkilby
0
110
Darren the Foodie - Storyboard
khoart
PRO
2
2.3k
Leading Effective Engineering Teams in the AI Era
addyosmani
9
1.6k
Transcript
NestJS A progressive web framework #kng9 @jiko_21 (a.k.a Daiki Kojima)
Grad Student @ Kyoto univ Frontend / Serverside Engineer Android
DeveloperͩͬͨΓ͢Δ Love: Vue.js, TypeScript, Kotlin, Golang… @jiko_21 (a.k.a Daiki Kojima) @jiko_21 @jiko21
Agenda • NestJSͬͯԿ? • ͔ΜͨΜͳ֓ཁ • ҰൠతͳApplicationΛॻ͘ͷʹඞཁͳࣝ • NestJSͷಛ •
·ͱΊ
2018 Warsaw @ PorlandͰ ͜Μͳηογϣϯ͕͋ͬͨ
JavaScript Conference’18 @Warsaw https://youtu.be/f0qzBkAQ3mk
NestJSͱ • Progressive Node.js framework • TypeScriptͰهड़Մೳ • ༷ʑͳπʔϧ͕ఏڙ͞Ε͍ͯΔ
࣮ࡍʹதΛݟͯΈΔ
NestJSͷجૅ • module • ίʔυΛ·ͱΊͯػೳΛཧతͳ୯Ґʹׂ ˠ ࠶ར༻Մೳʹ! • શͯͷNestͷΞϓϦέʔγϣϯʹroot module͕ଘࡏ͠
ΞϓϦέʔγϣϯΛىಈͤ͞Δ (Angularͱಉ༷)
NestJSͷجૅ 3PPUNPEVMF 4VC NPEVMF جຊతʹ͍ΖΜͳͷΛ͜Εʹ ٧ΊࠐΊ͍ͯ͘
moduleΛߏ͢Δͷ
moduleΛߏ͢Δͷ $POUSPMMFST
moduleΛߏ͢Δͷ 1SPWJEFST $POUSPMMFST
moduleΛߏ͢Δͷ 1SPWJEFST $POUSPMMFST *NQPSUT
moduleΛߏ͢Δͷ 1SPWJEFST $POUSPMMFST *NQPSUT &YQPSUT
moduleΛߏ͢Δͷ @Module({ imports: [TypeOrmModule.forFeature([Todo]),], providers: [TodosService], controllers: [TodosController], exports: [],
}) export class TodosModule {}
moduleΛߏ͢Δͷ @Module({ imports: [TypeOrmModule.forFeature([Todo]),], providers: [TodosService], controllers: [TodosController], exports: [],
}) export class TodosModule {} !.PEVMFʹ ͦΕͧΕΛهೖ͍ͯ͘͠
Controllers
Controllers • ϧʔςΟϯάΛ୲͏ • pathͱmethod͕ ରԠ @Controller('todos') export class TodosController
{ constructor(private readonly todosService: TodosService) {} @Get() getTodos(): Promise<ITodo[]> { return this.todosService.findAll(); } @Get(':id') getTodo(@Param('id', new ParseIntPipe()) id: number): Promise<ITodo> { return this.todosService.findById(id); } @Post('') createTodo(@Body() todoDao: TodoDao): Promise<ITodo> { return this.todosService.create(todoDao) } … }
Controllers • ϧʔςΟϯάΛ୲͏ • pathͱmethod͕ ରԠ @Controller('todos') export class TodosController
{ constructor(private readonly todosService: TodosService) {} @Get() getTodos(): Promise<ITodo[]> { return this.todosService.findAll(); } @Get(':id') getTodo(@Param('id', new ParseIntPipe()) id: number): Promise<ITodo> { return this.todosService.findById(id); } @Post('') createTodo(@Body() todoDao: TodoDao): Promise<ITodo> { return this.todosService.create(todoDao) } … } ! Ξϊςʔγϣϯ Ͱ NFUIPEͱQBSBNɺQBUIΛઃఆ
• ϧʔςΟϯάΛ୲͏ • pathͱmethod͕ ରԠ Controllers @Controller('todos') export class TodosController
{ constructor(private readonly todosService: TodosService) {} @Get() getTodos(): Promise<ITodo[]> { return this.todosService.findAll(); } @Get(':id') getTodo(@Param('id', new ParseIntPipe()) id: number): Promise<ITodo> { return this.todosService.findById(id); } @Post('') createTodo(@Body() todoDao: TodoDao): Promise<ITodo> { return this.todosService.create(todoDao) } … }
• ϧʔςΟϯάΛ୲͏ • pathͱmethod͕ ରԠ Controllers @Controller('todos') export class TodosController
{ constructor(private readonly todosService: TodosService) {} @Get() getTodos(): Promise<ITodo[]> { return this.todosService.findAll(); } @Get(':id') getTodo(@Param('id', new ParseIntPipe()) id: number): Promise<ITodo> { return this.todosService.findById(id); } @Post('') createTodo(@Body() todoDao: TodoDao): Promise<ITodo> { return this.todosService.create(todoDao) } … } DPOTUSVDUPSʹґଘ͢Δ0CKFDUΛ ॻ͍͓ͯ͘ͱ.PEVMF͕ґଘΛղܾ %*ɺޙड़
Providers • providersͷίʔυ NestJSʹΑͬͯΠϯελϯεԽ ͞ΕͯModuleͰڞ༗ (DI) @Module({ imports: [TypeOrmModule.forFeature([Todo]),], providers:
[TodosService], controllers: [TodosController], exports: [], }) export class TodosModule {}
Providers @Injectable() export class TodosService { constructor(@InjectRepository(Todo) todoRepo: Repository<Todo>) {}
} @Controller('todos') export class TodosController { constructor(private readonly todosService: TodosService) {} } @Module({ imports: [TypeOrmModule.forFeature([Todo]),], providers: [TodosService], controllers: [TodosController], exports: [], }) export class TodosModule {}
Providers @Injectable() export class TodosService { constructor(@InjectRepository(Todo) todoRepo: Repository<Todo>) {}
} @Controller('todos') export class TodosController { constructor(private readonly todosService: TodosService) {} } @Module({ imports: [TypeOrmModule.forFeature([Todo]),], providers: [TodosService], controllers: [TodosController], exports: [], }) export class TodosModule {} *OKFDUBCMFΛ ࢦఆ
Providers @Injectable() export class TodosService { constructor(@InjectRepository(Todo) todoRepo: Repository<Todo>) {}
} @Controller('todos') export class TodosController { constructor(private readonly todosService: TodosService) {} } @Module({ imports: [TypeOrmModule.forFeature([Todo]),], providers: [TodosService], controllers: [TodosController], exports: [], }) export class TodosModule {} *OKFDUBCMFΛ ࢦఆ DPOUSPMMFSʹ ྲྀ͠ࠐΈ *OKFDU
Imports • ͜ͷModuleͰproviderͱ͔͕ඞཁͱ͍ͯ͠ΔModuleΛ ͜͜Ͱॻ͍ͯ͋͛Δ • TypeORMΈ͍ͨͳthird party module • ࣗͰॻ͍ͨΧελϜmodule
Imports @Module({ imports: [TypeOrmModule.forFeature([Todo]),], providers: [TodosService], controllers: [TodosController], exports: [],
}) export class TodosModule {} @Module({ imports: [ TodosModule ], controllers: [AppController], providers: [AppService], }) export class AppModule {}
Imports @Module({ imports: [TypeOrmModule.forFeature([Todo]),], providers: [TodosService], controllers: [TodosController], exports: [],
}) export class TodosModule {} @Module({ imports: [ TodosModule ], controllers: [AppController], providers: [AppService], }) export class AppModule {} JNQPSU͍ͯ͘͜͠ͱͰ .PEVMF͕͑Δ
ׂ͞ΕͨModuleΛ͓͏!
ͱ͍͑…
Serviceͱ͔ͬͯɺ ผͷModuleͰݺͼͨ͘ͳΔΑͶ…
exports • providerͷαϒηοτ • ͜͜ʹॻ͔Ε͍ͯΔͷɺ͜ͷmoduleΛimport͍ͯ͠Δ ଞͷmodule͔Βར༻͕Ͱ͖Δ!
JestͰςετ͕ॻ͚Δ
Jestͱ • FacebookͷJavaScript༻Testing Framework • ଞͷTesting Libraryͱҧ͍ɺAll in one •
ϑϩϯτΤϯυͩͱ… • React, Vue.jsͰΑ͘ΘΕΔ…
ॻ͖ํͷҰྫ describe('TodosService', () => { let service: TodosService; let repository:
Repository<Todo>; beforeEach(async () => { ... }); afterEach(async () => { ... }); it('findAll success', async () => { expect(await service.findAll()).toEqual(TODOS_ARAY); }); });
ॻ͖ํͷҰྫ describe('TodosService', () => { let service: TodosService; let repository:
Repository<Todo>; beforeEach(async () => { ... }); afterEach(async () => { ... }); it('findAll success', async () => { expect(await service.findAll()).toEqual(TODOS_ARAY); }); }); ֤ςετέʔεલʹ࣮ߦ
ॻ͖ํͷҰྫ describe('TodosService', () => { let service: TodosService; let repository:
Repository<Todo>; beforeEach(async () => { ... }); afterEach(async () => { ... }); it('findAll success', async () => { expect(await service.findAll()).toEqual(TODOS_ARAY); }); }); ֤ςετέʔεલʹ࣮ߦ ֤ςετέʔεޙʹ࣮ߦ
JestͰΑ͋͘Γ͕ͪͳ͜ͱ… • ୯ମςετͰɺ֎෦ϞδϡʔϧͷڍಈΛϞοΫ͍ͨ͠ • jest.spyOnͰϞοΫ it('getProducts', async () => {
jest.spyOn(service, ‘findAll') .mockResolvedValue(PRODUCTS_ARRAY); expect(await controller.getProducts()) .toBe(PRODUCTS_ARRAY); }); @Get() getProducts(): Promise<IProducts[]> { return this.productsService.findAll(); } ςετର ςετίʔυ
JestͰΑ͋͘Γ͕ͪͳ͜ͱ… • ୯ମςετͰɺ֎෦ϞδϡʔϧͷڍಈΛϞοΫ͍ͨ͠ • jest.spyOnͰϞοΫ it('getProducts', async () => {
jest.spyOn(service, ‘findAll') .mockResolvedValue(PRODUCTS_ARRAY); expect(await controller.getProducts()) .toBe(PRODUCTS_ARRAY); }); @Get() getProducts(): Promise<IProducts[]> { return this.productsService.findAll(); } ςετର ςετίʔυ ͍ͭ͜ͷڍಈΛNPDL͍ͨ͠
JestͰΑ͋͘Γ͕ͪͳ͜ͱ… • ୯ମςετͰɺ֎෦ϞδϡʔϧͷڍಈΛϞοΫ͍ͨ͠ • jest.spyOnͰϞοΫ it('getProducts', async () => {
jest.spyOn(service, ‘findAll') .mockResolvedValue(PRODUCTS_ARRAY); expect(await controller.getProducts()) .toBe(PRODUCTS_ARRAY); }); @Get() getProducts(): Promise<IProducts[]> { return this.productsService.findAll(); } ςετର ςετίʔυ ͍ͭ͜ͷڍಈΛNPDL͍ͨ͠ ͍ͭ͜ͷڍಈΛNPDL͢Δ
NestJSͩͱ • moduleʹΑͬͯґଘղܾΛߦ͏ͷͰɺͦ͜ͰmockΛ ྲྀ͠ࠐΜͰ͠·͑OK describe('Todos Controller', () => { let
controller: TodosController; let service: TodosService; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [{ provide: TodosService, useValue: todoServiceMock, }], controllers: [TodosController], }).compile(); controller = module.get<TodosController>(TodosController); service = module.get<TodosService>(TodosService); }); }); export const todoServiceMock = { findAll: () => TODOS_ARAY, findById: (id: number) => TODOS_ARAY[0], create: (todoDao: TodoDao) => todoDao, }; mockؔ ςετίʔυ
Ұ୴ɺجૅతͳͱ͜Ζݟͨ!
NestJSͰ։ൃ͢Δ ͏·ΈͬͯԿ?
ओͳಛ 1. TypeScriptͰͷهड़͕default 2. πʔϧ܈͕๛ 3. όοΫΤϯυ (http provider) ΛબՄೳ
TypeScriptͰͷهड़͕default • cliʹΑͬͯੜ͞ΕͨίʔυશͯTypeScriptͰॻ͔Ε͍ͯΔ • ଞͷFrameworkͩͱ(Express…) • ࣗલͰTypeScriptԽ͕ඞཁ • ͱ͍͑JavaScriptͰॻ͚Δ
πʔϧ܈͕๛ • ৭ʑͳͷ͕ఏڙ͞Ε͍ͯΔ • DI, test util, microservice support, WebSockets,
RxJS • ʮNestJSϓϥοτϑΥʔϜͰ͋Δʯɺͱ͍͏ࢥ͔Β ͜ͷΑ͏ʹͳ͍ͬͯΔ
όοΫΤϯυ (http provider) ΛબՄೳ • package.jsonͱpackage-lock.jsonΛݟͯΈΔ { "name": "todo-app", "dependencies":
{ "express": { "version": "4.17.1", "resolved": “...”, "integrity": “...“, "requires": { ... }, "dependencies": { ... } }, } } { "name": "todo-app", "version": "0.0.1", "scripts": {}, "dependencies": { "@nestjs/common": "^6.7.2", "@nestjs/core": "^6.7.2", "@nestjs/platform-express": "^6.7.2", ... }, "devDependencies": { "@nestjs/cli": "^6.9.0", "@nestjs/schematics": "^6.7.0", "@nestjs/testing": "^6.7.1", "@types/express": "^4.17.1", ... }, }
όοΫΤϯυ (http provider) ΛબՄೳ • package.jsonͱpackage-lock.jsonΛݟͯΈΔ { "name": "todo-app", "dependencies":
{ "express": { "version": "4.17.1", "resolved": “...”, "integrity": “...“, "requires": { ... }, "dependencies": { ... } }, } } { "name": "todo-app", "version": "0.0.1", "scripts": {}, "dependencies": { "@nestjs/common": "^6.7.2", "@nestjs/core": "^6.7.2", "@nestjs/platform-express": "^6.7.2", ... }, "devDependencies": { "@nestjs/cli": "^6.9.0", "@nestjs/schematics": "^6.7.0", "@nestjs/testing": "^6.7.1", "@types/express": "^4.17.1", ... }, }
όοΫΤϯυ (http provider) ΛબՄೳ • ࣮NestJSͷhttp providerࣗલͷͷͰͳ͍ • σϑΥϧτExpress •
ଞͷhttp providerར༻͢Δ͜ͱ͕Մೳ • Fastifyͱ͔ → expressͷ2ഒ΄ͲϕϯνϚʔΫͰྑ͍ੑೳ • ͜ΕʹΑΓ༷ʑͳthird-party plugin͕ར༻Մೳʹ
fastifyʹมߋ • ҎԼͷίϚϯυͰಋೖ • main.tsΛҎԼͷΑ͏ʹ͢Δ $ npm i @nestjs/platform-fastify import
{ NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { FastifyAdapter } from '@nestjs/platform-fastify'; async function bootstrap() { const app = await NestFactory.create(AppModule, new FastifyAdapter()); await app.listen(3000); } bootstrap();
͡Ό͋ͳΜͰ
σϑΥϧτ͕expressͳͷ͔…
৭ʑཧ༝͋Γ • express͕͘ΘΕ͍ͯͯmiddleware͕ଟ͍ • Nested routerʹରԠ͍ͯ͠ͳ͍ͨΊɺSub-Domain routing ʹରԠ͍ͯ͠ͳ͍ • sub-domain
routingΛ༗ޮʹ͢ΔͱexpressʹΓସΘΔ
·ͱΊ • NestJSmodule୯ҐͰͷϩδοΫׂΛجຊͱ͓ͯ͠Γɺ ࠶ར༻ੑͷߴ͍ίʔυ͕ॻ͚Δ • TypeScriptͷσϑΥϧταϙʔτɺDIͷඪ४ఏڙͳͲɺ αϙʔτ͕ڧ͍ (TypeScript൛Spring BootͱݴͬͯաݴͰͳ͍)
Reference • Building a platform: NestJS from the ground up
| Kamil Myśliwiec | jsPoland 2018 https://youtu.be/f0qzBkAQ3mk • Document of NestJS: https://docs.nestjs.com/
ऴ ੍࡞ɾஶ࡞ ᴸᴸᴸᴸᴸ @jiko_21