MEANスタック入門(4) AngularとExpressの連携
今回はExpressで作成したAPIをAngularから実際に利用しつつ、Angularの基本的な開発方法に触れていきます。
project-name/src/app
配下で作業し、作業後の構成は以下のようになります。
app │ app-routing.module.ts │ app.component.html │ app.component.scss │ app.component.spec.ts │ app.component.ts │ app.module.ts │ ├─components │ └─call-express-api │ call-express-api.component.html │ call-express-api.component.scss │ call-express-api.component.spec.ts │ call-express-api.component.ts │ └─services call-express-api.service.spec.ts call-express-api.service.ts
Serviceの作成
Angularでは、各種APIへのアクセスはServiceから行います。
services
ディレクトリを作成し、Angular CLIのng g service
コマンドで必要ファイルを生成します。
ng g service
コマンドはカレントディレクトリに依存するので、services
ディレクトリまで移動してから実行します。
$ ng g service callExpressApi
実行すると以下の2ファイルが生成されます。
spec
のほうはテスト用なので、call-express-api.service.ts
のほうにAPIにアクセスするgetUsers
メソッドを実装します。
まず、HTTPアクセスに必要なHttp
クラスをインポートしてコンストラクタの引数に指定します。
また、Http.get
はObservable型の値を返すので、Observable.map
を使うためにrxjs/add/operator/map
をインポートします。
call-express-api.service.ts
import { Injectable } from '@angular/core'; import { Http } from '@angular/http'; import 'rxjs/add/operator/map'; @Injectable() export class CallExpressApiService { constructor( private http: Http ) { } getUsers() { return this.http.get("api/users") .map(response => response.json()); } }
上記ではapi/users
にアクセスしていますが、ng server
で実行している場合、Angularの簡易サーバとAPIサーバが別のポートで動いているので、Angular側からAPIを呼ぶと404のエラーになります。
また、ポートまで指定してAPIにアクセスした場合も、クロスオリジンのエラーで同じく実行出来ません。
これを回避するには、プロジェクトのルートディレクトリにproxy.conf.json
を作成し、ng server
実行時に--proxy-config
オプションで読み込ませます。
proxy.conf.json
{ "/api": { "target": "http://localhost:4300", "secure": false } }
$ ng server --proxy-config proxy.conf.json
これでポートの差異を意識せず開発出来るようになります。
Componentの作成
Componentはビューの部品のようなもので、各部品毎の表示や動作を定義します。
components
ディレクトリを作成し、Angular CLIのng g component
コマンドで必要ファイルを生成します。
ng g component
コマンドはng g service
と同じくカレントディレクトリに依存するので、components
ディレクトリまで移動してから実行します。
$ ng g component callExpressApi
実行するとcall-express-api
ディレクトリが生成され、その配下に以下4ファイルが生成されます。
- call-express-api.component.html
- call-express-api.component.scss
- call-express-api.component.ts
- call-express-api.component.spec.ts
call-express-api.component.ts
CallExpressApiComponent
クラスを作成し、先ほど作成したCallExpressApiService
を実行して、HTML表示用の変数に代入します。
CallExpressApiService
をインポートしてコンストラクタの引数に指定し、Serviceのインスタンスを生成するためにproviders
で読み込みます。これは、app.module.ts
でグローバルに読み込んでも動きますが、今回はこのComponentだけなのでここで読み込みます。
getUsers
はObservableを返すので、subscribe
メソッドでデータを受け取って変数users
に代入します。
call-express-api.component.ts
import { Component, OnInit } from '@angular/core'; import { CallExpressApiService } from '../../services/call-express-api.service'; @Component({ selector: 'app-call-express-api', templateUrl: './call-express-api.component.html', styleUrls: ['./call-express-api.component.scss'], providers: [CallExpressApiService] }) export class CallExpressApiComponent implements OnInit { users: any; constructor( private callExpressApiService: CallExpressApiService ) { } ngOnInit() { this.callExpressApiService.getUsers().subscribe(u => { this.users = u.users; }); } }
call-express-api.component.html
APIからの返答が代入された変数users
を利用して、内容を表示するHTMLを作成します。
配列を受けて要素の数だけ繰り返し表示させる場合、ngFor
ディレクティブを利用します。*
プレフィックスを付与することでtr
要素がテンプレート化され、そのテンプレートを元にusers
の各要素の内容が表示されます。
call-express-api.component.html
<table border="1"> <tr *ngFor="let user of users"> <td>{{user.id}}</td> <td>{{user.address}}</td> </tr> </table>
ルーティングの設定
初めに表示されるComponentはapp
直下にあるapp.component.html
です。
ここにナビゲーションを用意し、ナビゲーションをクリックするとヘッダーを残してコンテンツ部分が切り替わるような、いわゆるシングルページアプリケーションの構成にします。
routerLink
ディレクティブでURIを指定し、<router-outlet>
タグでコンテンツ表示位置を設定します。
app.component.html
<header> <h1 class="logo"><a href="/">MEAN</a></h1> <nav> <ul> <li><a routerLink="/users">Users</a></li> </ul> </nav> </header> <router-outlet></router-outlet>
Angularアプリをng new
で初めに生成する際、--routing=true
を付けているとapp
直下にapp-routing.module.ts
が生成され、app.module.ts
で既に読み込まれています。
このapp-routing.module.ts
に、ルーティングさせたいURIとComponentを記述します。
app-routing.module.ts
import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { CallExpressApiComponent } from './components/call-express-api/call-express-api.component'; const routes: Routes = [ {path: 'users', component: CallExpressApiComponent}, ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }
動作確認
ng server
で実行している場合のAngular側のテストは、http://localhost:4200にアクセスします。
Usersのリンクをクリックすると、APIのレスポンスを受けてテーブルが表示されます。
ビルド後はdist
ディレクトリのclient
配下のファイルも揃うので、dist/server/bin/www.js
を実行するだけでhttp://localhost:4300からclient
配下のファイルも提供されます。
MEANスタック入門(3) Expressを利用したバックエンド開発
今回はAngular CLIで作成したAngularのディレクトリ階層を足場にして、Expressを活用したバックエンドの開発を行います。
言語はTypeScriptで書いていきますので、トランスパイルの方法などにも触れていきます。
TypeScriptのインストール
基本的にコードはTypescriptで書くのでtypescript
をインストールします。
--save-dev
オプションを指定すると、package.json
のdevDependencies
に書き込まれます。
npm install --save-dev typescript
ローカルインストールしたのでtsc
はnode_modules/.bin/
配下にあります。
node_modules/.bin/
にパスを通しておくか、以下のように実行します。
$ node_modules/.bin/tsc
ここから先はパスを通した前提で進めます。
Expressのインストールとバックエンドの実装
まずはexpress
とリクエストデータを処理するためのbody-parser
をインストールします。
--save
オプションを指定すると、package.json
のdependencies
に書き込まれます。
$ npm install --save express body-parser
ルートディレクトリにserver
ディレクトリを作成し、以下のような構成にします。
server │ app.ts │ config.ts │ tsconfig.json │ ├─bin │ www.ts │ └─routes users.ts
エントリポイントはbin
配下のwww.ts
です。
server
直下のapp.ts
とconfig.ts
を参照します。
www.ts
import * as http from "http"; import { app } from "../app"; import { serverPort } from "../config"; const port = process.env.PORT || serverPort; app.set("port", port); const server = http.createServer(app); server.listen(port);
設定ファイルconfig.ts
にポート番号を記載します。
config.ts
export const serverPort = 4300;
www.ts
の中でhttp.createServer
に渡しているapp.ts
には、サーバーでリクエストを受け取った時の処理を書きます。
ルーティングはここで実行し、実際の処理はroutes
配下のファイルに振ります。
ちなみに開発中にAngularをng server
で実行している場合はdist/client
は空なので、ルートにアクセスしても404になります。
app.ts
import * as express from "express"; import * as path from "path"; import { json, urlencoded } from "body-parser"; import * as compression from "compression"; import { usersRouter } from "./routes/users"; const app: express.Application = express(); app.disable("x-powered-by"); app.use(json()); app.use(compression()); app.use(urlencoded({ extended: true })); app.use(express.static(path.join(__dirname, "../client"))); app.use("/api/users", usersRouter); app.get("/", (req, res) => { res.sendFile(path.join(__dirname, '../client/index.html')); }); export { app };
users.ts
を作成してapiの実体を書きます。
ここでは/api/users
にGETでアクセスがあった場合、ダミーデータのjsonを返すようにします。
users.ts
import { Request, Response, Router } from "express"; const usersRouter: Router = Router(); let users = {users: [ { id: 0, address: "NA6IF2OMBSTK2NEMXMYDQYXT32IGPKHSMULWLAKE" }, { id: 1, address: "NDSNEM2HC4IEPQXGVDOWYMJ2MIPENUXN6HFJDHYZ" } ]}; usersRouter.get("/", (request: Request, response: Response) => { response.json(users); }); export { usersRouter };
トランスパイルとサーバーの実行
これでロジックは完成なので、トランスパイルの設定をserver
直下のtsconfig.json
に書きます。
outDir
で出力先を設定します。
tsconfig.json
{ "compilerOptions": { "declaration": false, "emitDecoratorMetadata": true, "experimentalDecorators": true, "lib": ["es6", "dom"], "module": "commonjs", "moduleResolution": "node", "outDir": "../dist/server", "target": "es6", "typeRoots": [ "../node_modules/@types" ] } }
tsc
は-p
オプションで上記で作成したtsconfig.json
の位置を指定して実行します。
$ tsc -p ./server
トランスパイルされたwww.jsを実行すると、config.ts
に設定したポートでサービスが提供されます。
$ node dist/server/bin/www.js
動作確認
http://localhost:4300/api/usersにアクセスすると、apiからレスポンスが返ってきます。
これでExpress側の準備は一旦完了です。
MEANスタック入門(2) Angularを利用したフロントエンド開発
今回はAngular CLIを利用したAngularのHello World的な内容になります。
Angular CLIのインストール
Angular2アプリの開発には、必須という訳ではありませんがAngular CLIを利用するのがおすすめです。
ディレクトリ構成を自動的に整えてくれますし、各種Componentのひな型の生成、簡易サーバによる高速なプレビューなど、非常に効率的に開発出来ます。
今後も基本的にAngular CLIで生成されたディレクトリを足場にして以降の開発を進めていきます。
まずはangular-cli
をインストールします。
$ npm install -g @angular/cli $ ng -v @angular/cli: 1.0.1 node: 6.9.4 os: win32 x64
Angular2アプリの作成と実行
次にAngular2アプリを作成します。
CSSではなくSCSSなどの言語を利用したい場合は--style
オプションで切り替え、ルーティングモジュール付きで生成する場合は--routing
オプションで設定します。
ルーティングモジュールはapp.module
に直接書くケースもあるようですが、別ファイルに分けたほうが後々開発しやすいと思うので、true
で実行します。
$ ng new project-name --style=scss --routing=true
作成したアプリをng server
コマンドで実行します。
$ cd project-name
$ ng server
実行するとプロジェクトがビルドされ、簡易サーバが立ち上がります。
動作確認
http://localhost:4200にアクセスすると、動作が確認出来ます。
同時にソースを監視するようになっているので、コードを書き換えるたびに自動的にリビルドされて非常に便利です。
Angular2アプリのビルド
実行中のアプリを本番環境用にビルドします。
デフォルトだと、ビルドされたファイルはdist
ディレクトリに配置されます。
後ほどdist
ディレクトリをサーバ側と共有する予定なので、.angular-cli.json
のapps
にあるoutDir
を書き換えて出力先を変更します。
"outDir": "dist/client"
ng build
でビルドします。
--prod
オプションを付けると公開用にminifyされたファイルが生成されます。
$ ng build --prod
これでAngular2側の準備は一旦完了です。
MEANスタック入門(1) MEANスタックとは
Angularにハマってしばらく色々と実験していたものの、フロントエンド開発だけではやれないことが出てきたので、バックエンド開発も含めて一通り触ってみようと思い立ちました。
当然のようにMEANスタックに辿り着き、関連する様々な記事を読んでみましたが、イマイチ最新かつ全体を網羅した良い感じの記事に出会えませんでした。 例えば、Angularが古いAngularJSであったり、Angular CLIを使っていなかったり、また、TypeScriptではなかったり。
今更AngularJSは使いたくありませんし、Angular CLIを使いたいので、そうなるとフロントエンドもバックエンドもTypeScriptで統一したいです。ただ、資料がないということは、もしかしたら主流の構成じゃないのかもしれませんが。。
結構サイトによってフォルダ構造などもまちまちで、何が本当に最適な手法かは分からなかったのですが、GitHubなどを見ながら正解を探し求め、最終的にとりあえず辿り着いたそれっぽい結論を、自分用の備忘録も兼ねてまとめておきます。
前提知識
筆者の事前知識は以下のような状況なので、ほぼ入門者と変わらないと思われます。
- JavaScriptは割と昔から触ったことがあった。
- TypeScriptや、いわゆるモダンJavaScriptには最近出会った。
- 最近AngularJSを初めて触り、その流れでAngular2を調べ始めた。
- Angularに触るまでNode.jsは触ったことがなかった。
- ExpressやMongoDBは今回初めて触った。
よって、とりあえず動かしてみるというレベルであれば、前提となる知識はJavaScriptを理解している程度で良いかと思います。
MEANスタックとは
MEANスタックとは、MongoDB、Express、Angular、Node.jsの頭文字をとったもので、4つを組み合わせてバックエンドからフロントエンドまで全部JavaScriptで開発してしまおうというものです。
複数の言語を行き来しなくて良いというのはかなり魅力的です。
MongoDB
JSONやJavaScriptと親和性が高い、NoSQL型ドキュメント指向データベース。
Express
Node.js上で動作するWEBアプリケーションのフレームワーク。 MEANスタックで利用される場合には、主にAPIの実装などバックエンド開発を担う。
Angular
フロントエンドのフレームワーク。モダンJavaScriptの教科書とも言われ、数年ぶりにJavaScriptに触るというような人にはもってこいの教材。
Node.js
サーバーサイドJavaScriptの実行環境。
事前準備
Node.jsのインストール
OSによってまちまちだと思うので、以降はNode.jsはすでに入っている前提で進めます。
入っていなければ公式からインストールします。
パスが通ってなければ通しておきます。
自分がインストールしたバージョンは以下です。
$ node -v
v6.9.4
MongoDBのインストール
同じく、以降はMongoDBはすでに入っている前提で進めます。
入っていなければ公式からインストールします。
インストールフォルダ(自分の環境だとC:/MongoDB/Server/3.4/bin
)にパスを通しておきます。
インストールしたバージョンは以下です。
$ mongod --version db version v3.4.4 git version: 888390515874a9debd1b6c5d36559ca86b44babd OpenSSL version: OpenSSL 1.0.1u-fips 22 Sep 2016 allocator: tcmalloc modules: none build environment: distmod: 2008plus-ssl distarch: x86_64 target_arch: x86_64
データベースは任意のディレクトリに保存出来るので、bin
直下にmongodb.config
を作成して、データベースの保存場所を指定します。
mongodb.config
dbpath=C:/MongoDB/data
上記を指定してmongod
を起動します。
$ mongod --config "C:/MongoDB/Server/3.4/bin/mongodb.config"
これでデータベースが起動しました。
次回からは実際にMEANスタックを利用した開発に入っていきます。