Skip to content

Instantly share code, notes, and snippets.

@mitsuruog
Last active January 24, 2025 02:47
Show Gist options
  • Select an option

  • Save mitsuruog/fc48397a8e80f051a145 to your computer and use it in GitHub Desktop.

Select an option

Save mitsuruog/fc48397a8e80f051a145 to your computer and use it in GitHub Desktop.
express実践入門

express実践入門

  • 2015/10/29
  • 株式会社フレクト
  • テクニカルスペシャリスト
  • 小川 充

自己紹介

小川充

  • フロントエンドエンジニア

    • Javascript, HTML, CSS, Node.js, API設計とか認証とか
  • 2015/5月入社(約半年)

  • API開発でnodeを使いたい。そのためにはnodeを使える人を増やす必要がある!!

    • JavaでAPI開発はもういやや。。。

はじめに

あくまで「俺が考える最強のexpress実践入門」です。

初学者がexpressを攻略する上でのつまづくポイントと、中規模開発をターゲットにしたベストプラクティスを経験ベースでお話します。

おそらく、〜初級者向けの内容です。


本コンテンツの使い方

  • express初心者
    • 初学者向けチュートリアル(dotinstallとか)のお供に
  • express経験者
    • ご自身のコードの見直しに
  • 他の言語の経験者
    • 他の言語の「あれ」は、node.jsでは「これ」のマッピングに

(※)中で紹介するコードは抜粋したものであり、そのままでは動作しない場合があります。ご注意ください。
(※)versionはnode v4.2.0, express v4.13.1です。


expressの(超)概要

expressとはなにか?expressの初め方について

(ググったらすぐ出てくる内容ですよ〜)


express

Fast, unopinionated, minimalist web framework for Node.js

  • Fast - 高速
  • unopinionated - オープン
  • minimalist - 軽量

Node.jsのための、高速で軽量でオープンなWebフレームワーク。


Why express??

  • ほぼデファクトの地位
  • 豊富な情報量、サンプル
  • 豊富な拡張機能(middleware)
  • Pure node.jsで作成した場合、アプリを仕上げていく過程で「そもそもexpressで良かったのでは?」となることが多かった(経験談)

(※)ただし、3系と4系の違いに注意。世の中のサンプルは3系で書かれているものが多く、動作しないことがある。

他のWebフレームワーク


Install

Node.jsをインストールして、、、
ほぼ一発。

mkdir myapp && myapp
npm init
npm install express

Hello world

サーバー側のコード(app.js)

var express = require('express');
var app = express();

// HTTPリクエストを受け取る部分
app.get('/', function (req, res) {
  res.send('Hello World!');
});

// サーバーを起動する部分
var server = app.listen(3000, function () {
  var host = server.address().address;
  var port = server.address().port;
  console.log('Example app listening at http://%s:%s', host, port);
});

サーバーを起動して、http://localhost:3000にアクセス

node app.js
curl http://localhost:3000 -> Hello World!

express-generator

通常はこちらの方をよく使います。Express application generator

(sudo)npm install express-generator -g

// expressコマンドでアプリのひな形を生成します
express myapp

   create : myapp
   create : myapp/package.json
   create : myapp/app.js
   ...
   create : myapp/bin
   create : myapp/bin/www
   
cd myapp 
// 依存モジュールをインストールします
npm install
// サーバーを起動します
node bin/www // or npm run start 

express-generatorプロジェクト構成

基本最小構成。後ほどオレ色に染め上げて行きます。

.
├── app.js				// expressサーバーの設定
├── bin
│   └── www				// サーバーの起動
├── package.json
├── public				// 静的ファイル置き場
│   ├── images
│   ├── javascripts
│   └── stylesheets
│       └── style.css
├── routes				// サーバー側のコントローラ
│   ├── index.js
│   └── users.js
└── views					// サーバー側で画面を作成する際のテンプレート
    ├── error.jade
    ├── index.jade
    └── layout.jade

express攻略

expressのここを理解すればOK!


express攻略の勘所

expressを理解する上での最小構成要素。

  • routing
  • middleware

以上2つ


expressの仕組み

「図」

  • routing
    • 外部からのHTTP(S)リクエストに対して、内部のロジックをマッピングすること。
  • middleware
    • routingの過程で何らかの処理を差し込む仕組み。
    • 共通処理(認証、エラーハンドリング、リクエストデータの加工、etc)を本来のロジックから分離して、コードベースを健全に保つ。

routing(1/2)

基本(Route paths, method, handler)

HTTPメソッド、Path、マッピングする内部ロジックを指定する方式。

var app = express();

// GET http://localhost:3000/
app.get('/', (req, res) => {});

// POST http://localhost:3000/books
app.post('/books', (req, res) => {});

// PUT http://localhost:3000/books/1
app.put('/books/:id', (req, res) => {});

// DELETE http://localhost:3000/books/1
app.delete('/books/:id', (req, res) => {});

routing(2/2)

基本(express.Router)

routing用のmiddlewareを作る仕組み。 routing部分をモジュール化(別ファイル化)することが多いため、こちらの方をよく利用します。

routingをモジュール化(router.js)

var app = express();
var router = express.Router();

router.get('/:id', (req, res) => {
	// 何かの処理
});

module.exports = router;

モジュールを利用する。(app.js)

var router = require('./router');
...
app.use('/books', router); 

http://localhost:3000/books/1」のroutingが有効になる


routing(Request method)

よく利用するもの

router.get('/', (req, res) => {
	// 何かの処理
});
  • req.body
    • request bodyのkey-valueペア(body-parser middlewareが必要)
  • req.cookies
    • cookieのkey-valueペア(cookie-parser middlewareが必要)
  • req.params
    • /books/:id/books/1の場合req.params.id => 1
    • url pathパラメータのkey-valueペア
  • req.query
    • /books?order=ascの場合req.query.order => asc
    • リクエストパラメータのkey-valueペア
  • req.get
    • HTTPヘッダーの値を取得する

routing(Response method)

よく利用するもの

router.get('/', (req, res) => {
	// 何かの処理
});
  • res.cookie
    • cookieを付与
  • res.set
    • HTTPヘッダーを付与
  • res.redirect
    • 指定したPathへリダイレクト
  • res.render
    • テンプレートエンジンを利用して画面を生成して返却
  • res.sendStatus
    • ステータスコードを返却(401, 404, 500, etc...)
    • ex) res.sendStatus(401).json({...})
  • res.json
    • jsonを返却(200)

middleware(基本)

middlewareのhandler(実体)の基本I/Fの形 (ただし、エラーハンドラを除く)

function(req, res, next) {
  // middlewareの処理
  next();
}

middlewareは1つのroutingに対して複数連結して処理されるため、次のmiddlewareへ移動するためにnextを利用する。
middlewareの実行順序は宣言したもの順。エラーハンドラが最後にあるのは、それなりの理由がある。

middlewareは3種類ある

  • Application-level
  • Router-level
  • Error-handling

middleware(Application-level)

Application-level

var app = express();

// '/'に対するmiddleware
app.use(function (req, res, next) {
  next();
});

// 'GET books/:id'に対するmiddleware
app.get('books/:id', function (req, res, next) {
  next();
});

middleware(Router-level)

Router-level

var router = express.Router();

// '/'に対するmiddleware
router.use(function (req, res, next) {
  next();
});

// 'GET books/:id'に対するmiddleware
router.get('books/:id', function (req, res, next) {
  next();
});

middleware(TPO)

Application-level middlewareとRouter-level middlewareの違いについて、利用者レベルでは正直良くわからない。

使い分け方針(TPO)

  • アプリ全体
    • Application-level middleware
    • app.use()
  • 特定のrouting
    • Router-level middleware
    • router.get('/:id', someMiddleware, businessLogic)

middleware(Error-handling)

Error-handling

(後述) エラーハンドリングの部分で紹介します。


中規模Webアプリケーションを構築するために


** Webアプリケーションを作成するために必要なこと

  • プロジェクトストラクチャ
    • api
    • view

静的ページ


サーバーサイドレンダリング

  • Jade
  • handlebars

DB関連(ORMapper)

  • mongoose
  • sequalize
  • エラーハンドリング

認証(Passport)

Passport

  • ほぼ、node.jsの認証モジュールでデファクト
  • 様々な認証に対応可能(Strategy)
  • 下手に独自で認証を実装するくらいなら、Passportの使い方を習得したほうが後々潰しが効く(経験談)

対応例)


Passportのフォルダ構成

config
  app.js              - passportの初期化
  passport.js         - sessionのserialize/deserialize、利用するStrategyの設定
  passport/           - Strategyごとの認証ロジック
    local.js          - Username and password用
    twitter.js        - twitter認証用
  middlewares
    authorization.js  - routingで利用する認証フィルタ

passportの使い方(1/3)

Session用のmiddlewareを設定する。

app.js

var passport = require('passport');

// passportモジュールをLoad
require('./passport')(app);

// session用のmiddlewaresを有効化
app.use(passport.initialize());
app.use(passport.session());

passportの使い方(2/3)

Session利用のための設定(続き)&利用するStrategyを設定する

config/passport.js

module.exports = () => {

  // sessionにユーザー(のキー)情報を格納する処理
  passport.serializeUser((user, done) => {
    done(null, user.id);
  });
  
  // sessionからユーザー情報を復元する処理
  passport.deserializeUser((id, done) => {
    // DBのUserテーブルからユーザーを取得する処理
    User.findById(id).exec((err, user) => {
      done(err, user)
    });
  });

  // 利用するstrategyを設定
  passport.use(require('./passport/local'));
  passport.use(require('./passport/twitter'));
  ...

}

passportの使い方(3/3)

Strategyの個別設定を行う

config/passport/local.js

// Strategyをロードする
var LocalStrategy = require('passport-local').Strategy;
// Strategyことの認証ロジックを追加する
module.exports = new LocalStrategy({
  // 認証ロジック
});

(ログインなど)特定のrouting時に認証を行うようにする。

app.post('/login', 
  passport.authenticate('local', { failureRedirect: '/login' }),
  function(req, res) {
    res.redirect('/');
  });

Passport(認証フィルタ)

passportの作者が作ったものがある。
connect-ensure-login

予めmiddleware化しておく。 config/middlewares/authorization.js

// 認証フィルタに引っかかると`/badLoginRedirectPath`にリダイレクト
exports.authorize = require('connect-ensure-login').ensureLoggedIn('/badLoginRedirectPath');

routingでmiddlewareを設定する

var auth = require('middlewares/authorization');
app.use('/some', auth.authorize, (req, res) => {
  // 何かの処理
});

設定情報

  • 動作環境ごとで異なる情報(DB接続設定、APIKey、Secret)
  • production環境の設定はリポジトリにCommitせず、動作環境の環境変数から取得する

node.jsでの環境変数の取得方法

prosess.env.SOME_KEY
  • セッション
  • ロギング
  • デプロイ(Heroku)
  • 起動、監視

小ネタ

レスポンスのx-powered-byヘッダーを消す。

app.set('x-powered-by', false);

enjoy express :)

next your turn.

@mitsuruog
Copy link
Author

mitsuruog commented Oct 29, 2015

logo

@mitsuruog
Copy link
Author

img_20151029_095825_20151029100001532

@hlcq
Copy link

hlcq commented Aug 31, 2016

cool! thank you

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment