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
jiko21
February 12, 2020
Technology
3
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
Creating a Next.js-style Framework with Bun and Hono
jiko21
0
89
Array Grouping will soon be arriving at TypeScript
jiko21
0
85
Copying Array Methods arrived at TypeScript
jiko21
1
550
SSRで動的に OGP画像を生成したい! 〜Cloudflare Workersから@vercel/og移行編〜
jiko21
0
110
node:test will replace Jest?
jiko21
0
70
どこでも動かすために… TypeScriptでライブラリ開発の すゝめ
jiko21
2
310
レガシーなフロントエンドをリプレイスする
jiko21
5
1.5k
Deep Dive Into Vue Composition API
jiko21
0
3.2k
Composition API TypeScriptはVue.jsの夢を見るか?
jiko21
1
1.5k
Other Decks in Technology
See All in Technology
JAWS-UG20250116_iOSアプリエンジニアがAWSreInventに行ってきた(真面目編)
totokit4
0
140
[IBM TechXchange Dojo]Watson Discoveryとwatsonx.aiでRAGを実現!事例のご紹介+座学②
siyuanzh09
0
110
深層学習と3Dキャプチャ・3Dモデル生成(土木学会応用力学委員会 応用数理・AIセミナー)
pfn
PRO
0
460
30分でわかる「リスクから学ぶKubernetesコンテナセキュリティ」/30min-k8s-container-sec
mochizuki875
3
450
AWS Community Builderのススメ - みんなもCommunity Builderに応募しよう! -
smt7174
0
180
東京Ruby会議12 Ruby と Rust と私 / Tokyo RubyKaigi 12 Ruby, Rust and me
eagletmt
3
870
iPadOS18でフローティングタブバーを解除してみた
sansantech
PRO
1
140
Amazon Q Developerで.NET Frameworkプロジェクトをモダナイズしてみた
kenichirokimura
1
200
テストを書かないためのテスト/ Tests for not writing tests
sinsoku
1
170
I could be Wrong!! - Learning from Agile Experts
kawaguti
PRO
8
3.4k
今から、 今だからこそ始める Terraform で Azure 管理 / Managing Azure with Terraform: The Perfect Time to Start
nnstt1
0
240
2025年に挑戦したいこと
molmolken
0
160
Featured
See All Featured
Fashionably flexible responsive web design (full day workshop)
malarkey
406
66k
Optimizing for Happiness
mojombo
376
70k
個人開発の失敗を避けるイケてる考え方 / tips for indie hackers
panda_program
98
18k
Why You Should Never Use an ORM
jnunemaker
PRO
54
9.1k
How GitHub (no longer) Works
holman
312
140k
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
45
2.3k
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
127
18k
Fontdeck: Realign not Redesign
paulrobertlloyd
82
5.3k
What's in a price? How to price your products and services
michaelherold
244
12k
Java REST API Framework Comparison - PWX 2021
mraible
28
8.3k
Navigating Team Friction
lara
183
15k
Fight the Zombie Pattern Library - RWD Summit 2016
marcelosomers
232
17k
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