はじめに
Node.jsは、高速でスケーラブルなサーバーサイド開発を可能にする人気のプラットフォームです。そして、そのNode.jsをさらに活用できる強力なWebフレームワークが「Express」です。本記事では、Expressを使ってWebアプリケーションを開発する方法を初心者向けに解説します。これから始める方や、まだExpressに触れたことがない方でも安心して学べる内容となっています。まずはExpressのセットアップから、ルーティング、API作成の基本的な部分までを順を追って解説し、実践的な知識を身につけていきましょう。
Expressとは
Node.jsで使用される軽量で柔軟なWebアプリケーションフレームワークです。主にサーバーサイドのアプリケーション開発を簡単にするためのツールセットを提供します。Expressは、HTTPリクエストの処理、ルーティング、ミドルウェア(リクエストとレスポンスを操作する関数)の管理、テンプレートエンジンのサポートなど、Webサーバーに必要な基本的な機能を簡単に実装できます。
Expressの特徴
-
シンプルで軽量
Expressは非常にシンプルで、必要最低限の機能だけを提供しています。そのため、プロジェクトに合わせて自由に拡張しやすく、学習コストが低いです。 -
ルーティング機能
Expressは、特定のURLに対するリクエスト(GET, POST, PUT, DELETEなど)に対応するルート(エンドポイント)を簡単に定義できます。 -
ミドルウェアの活用
ミドルウェアはリクエストを処理する前に実行される関数で、認証、ロギング、エラーハンドリング、データのパースなど、リクエストに対する共通の処理を一元化できます。 -
テンプレートエンジンのサポート
Expressは、HTMLの動的生成をサポートするテンプレートエンジン(例えば、EJSやPugなど)と統合しやすく、HTMLページを動的に生成できます。 -
非同期処理対応
ExpressはNode.js上で動作しており、非同期I/Oに対応しています。そのため、パフォーマンスが高く、同時に多くのリクエストを処理できます。
Expressを使う利点
-
素早くアプリケーションを立ち上げられる
Expressを使うことで、設定や準備にかかる時間を短縮し、すぐにサーバーやAPIの開発に取り掛かれます。 -
大規模アプリケーションにも対応
Expressは小規模なアプリケーションから大規模なアプリケーションまで対応でき、拡張性に優れています。多くのプラグインやミドルウェアが公式・コミュニティによって提供されています。 -
広く使われている
ExpressはNode.jsのエコシステムの中でも非常に人気があり、多くの開発者によって使われています。そのため、リソースやドキュメントが豊富で学習しやすいです。
Express公式サイト
Expressの導入
まずはpackage.json
を作成します。
mkdir express-basics-guide
cd express-basics-guide
npm init -y
次にExpress
をインストールします。
npm i express
Express
の導入は以上となります。
サンプルコードの作成
簡単なコードを書いてExpress
が正しく動作するか確認します。
プロジェクト直下にserver.js
というファイルを作成します。
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.send('Hello, Express');
});
app.listen(3000, () => {
console.log('Server running on http://localhost:3000');
});
コードの解説をします。
const express = require('express');
const app = express();
Expressが提供している関数を実行しサーバー用のインスタンスを生成します。
app.get('/', (req, res) => {
res.send('Hello, Express');
});
GETメソッドで/
にアクセスされた際にHello, Express
という文字列を返します。
app.listen(3000, () => {
console.log('Server running on http://localhost:3000');
});
3000番で起動し起動後にコンソールログを出力し起動できていることを確認できるようにしておきます。
動作確認
Expressを起動します。
node server.js
どうやら起動できたようです。
ブラウザでhttp://localhost:3000/
にアクセスすると先ほど設定した文字列が表示されていることが確認できます。
都度ブラウザで確認するのが面倒という方はcurl
でもいいかと思います。
curl http://localhost:3000/
TypeScriptで開発
Expressが非常に簡単だというイメージができてきたところで少しずつ内容を深掘りしていきたいと思います。
まずはTypeScriptでコードを書くよう設定していきます。
必要なパッケージのインストール
TypeScriptで動かすために必要なパッケージをインストールします。
npm i -D typescript @types/node @types/express ts-node
-
typescript
TypeScriptのコンパイラです。TypeScriptコードをJavaScriptにトランスパイルするために必要です。プロジェクトでTypeScriptを使用する場合に必須のパッケージです。 -
@types/node
Node.jsのための型定義パッケージです。Node.jsの標準ライブラリ(fs, http, path など)をTypeScriptで使用する際に、型チェックや補完機能を提供します。 -
@types/express
Expressの型定義パッケージです。ExpressフレームワークをTypeScriptで使う際に、ルートやミドルウェアの型情報を提供し、補完や型チェックをサポートします。 -
ts-node
TypeScriptのコードを直接実行するためのツールです。通常はTypeScriptコードをコンパイルしてから実行しますが、ts-nodeを使うとコンパイルを事前に行わず、直接実行することができます。開発中のスクリプト実行に便利です。
tsconfigの作成
TypeScript プロジェクトのコンパイラ設定を管理するためのtsconfig.json
ファイルを作成します。
npx tsc --init
js -> tsに変更
拡張子を.js
から.ts
に変更しつつコードを修正します。
- const express = require('express');
+ import express from 'express'
const app = express();
app.get('/', (_req, res) => {
res.send('Hello, Express');
});
app.listen(3000, () => {
console.log('Server running on http://localhost:3000');
});
エディターで確認すると型が定義されていることがわかります。
動作確認
ts-node
でExpressを起動します。
npx ts-node server.ts
curl
でアクセスし設定した文字列が返ってくればOKです。
curl http://localhost:3000/
変更を監視
変更の都度コマンドを実行してExpressを起動するのは開発時に面倒なのでファイル監視ツールを使います。
npm i -D nodemon
起動コマンドが長くなるのでpackage.json
で定義します。
"scripts": {
+ "dev": "nodemon --watch '*.ts' --exec 'ts-node' server.ts",
"test": "echo \"Error: no test specified\" && exit 1"
},
下記コマンドで実行できるようになります。
npm run dev
server.ts
でレスポンス内容を変更するなど更新しファイルを保存すると自動でExpress
の再起動がかかります。
これでExpress
の開発が行いやすくなりました。
ルーティング
Express
の機能の一つであるルーティングについて幾つかご紹介します。
server.ts
に追記していく形を想定しています。
GETメソッド
/user
にGETメソッドでアクセス可能となります。
app.get('/user', (req, res) => {
res.send('Hello, User');
});
curl http://localhost:3000/user
Hello, User
POSTメソッド
app.post()
でPOSTメソッドが作成できます。
app.post('/submit', (req, res) => {
res.send('Form submitted!');
});
url -X POST http://localhost:3000/submit
Form submitted!
パラメータ付きルーティング
URLに動的なパラメータを含めることができます。
パラメータはreq.params
でアクセス可能ですのでリクエストに応じたレスポンスを返すことが可能です。
app.get('/users/:id', (req, res) => {
const userId = req.params.id;
res.send(`User ID: ${userId}`);
});
curl http://localhost:3000/users/123
User ID: 123
クエリパラメータ
URLにクエリ文字列を含める場合の処理です。
パラメータはreq.params
でアクセス可能ですのでクエリパラメータに応じたレスポンスを返すことが可能です。
app.get('/search', (req, res) => {
const query = req.query.q;
res.send(`Search query: ${query}`);
});
curl "http://localhost:3000/search?q=express"
Search query: express
ルーティングのグループ化
いくつもルートを定義していくと複雑になり保守性が下がります。
可能であればグループ化することで保守性を向上できます。
const router = express.Router();
router.get('/profile', (req, res) => {
res.send('User profile');
});
router.get('/settings', (req, res) => {
res.send('User settings');
});
app.use('/user', router);
curl http://localhost:3000/user/profile
User profile
curl http://localhost:3000/user/settings
User settings%
app.get('/test', (req, res) => {
res.send('Hello from Handler 1');
});
app.get('/test', (req, res) => {
res.send('Hello from Handler 2');
});
curl http://localhost:3000/test
Hello from Handler 1
ミドルウェア
Expressのミドルウェアは、リクエストとレスポンスの間で追加の処理を行う関数です。
ミドルウェアを使うことで、リクエストのログ記録、認証、データの加工、エラーハンドリングなど、アプリケーションの機能を拡張できます。
ミドルウェアの特徴
-
リクエストとレスポンスを操作可能
リクエスト (req) オブジェクトを変更したり、レスポンス (res) オブジェクトに追加の処理を行うことができます。 -
チェーン形式で処理を流す
next() を呼ぶことで次のミドルウェアやルートに処理を渡します。呼び出さないと処理が止まります。 -
順番が重要
ミドルウェアは登録された順に実行されます。順序を間違えると動作がおかしくなることがあります。
アプリケーションレベルのミドルウェア
全ての処理共通で設定したい場合。
ここではサーバー側でリクエストのメソッドとURLを出力し簡易的なログ記録を行っています。
// ログ記録用ミドルウェア
app.use((req, res, next) => {
console.log(`${req.method} ${req.url}`);
next();
});
リクエストに応じてサーバー側にログが出力されることが確認できます。
curl http://localhost:3000/
curl http://localhost:3000/test
curl -X POST http://localhost:3000/submit
ルーター(ルート)レベルのミドルウェア
特定のルートにのみ適用されるミドルウェアです。
app.get('/user/:id', (req, res, next) => {
console.log(`Request for user ID: ${req.params.id}`);
next();
}, (req, res) => {
res.send(`User ID: ${req.params.id}`);
});
curl http://localhost:3000/user/123
User ID: 123
起動したサーバー側には下記のログが出力されます。
Request for user ID: 123
ビルトインミドルウェア
Expressに組み込まれている便利なミドルウェアが存在します。
express.json()
はJSON形式のリクエストボディを解析します。
// JSON解析ミドルウェアを設定
app.use(express.json());
app.post('/api/data', (req, res) => {
console.log(req.body); // パースされたJSONデータ
res.send(`Received: ${JSON.stringify(req.body)}`);
});
curl -X POST http://localhost:3000/api/data \
-H "Content-Type: application/json" \
-d '{"name": "Alice", "age": 25}'
Received: {"name":"Alice","age":25}
誤った形式のJSONを送信するとエラーになります。
curl -X POST http://localhost:3000/api/data \
-H "Content-Type: application/json" \
-d '{"name": "Alice", "age": 25,'
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>SyntaxError: Unexpected end of JSON input<br> at JSON.parse (<anonymous>)<br> at parse (/Users/test/Documents/workspace/express-basics-guide/node_modules/body-parser/lib/types/json.js:92:19)<br> at /Users/test/Documents/workspace/express-basics-guide/node_modules/body-parser/lib/read.js:128:18<br> at AsyncResource.runInAsyncScope (node:async_hooks:203:9)<br> at invokeCallback (/Users/test/Documents/workspace/express-basics-guide/node_modules/raw-body/index.js:238:16)<br> at done (/Users/test/Documents/workspace/express-basics-guide/node_modules/raw-body/index.js:227:7)<br> at IncomingMessage.onEnd (/Users/test/Documents/workspace/express-basics-guide/node_modules/raw-body/index.js:287:7)<br> at IncomingMessage.emit (node:events:517:28)<br> at IncomingMessage.emit (node:domain:489:12)<br> at endReadableNT (node:internal/streams/readable:1400:12)</pre>
</body>
</html>
注意点
エラーハンドリング
先ほどの章でJSONの形式が不正な場合にエラーメッセージが返却されていました。
こちらでもエラー内容を確認できますが一般的にはエラーハンドリングを定義しておきます。
import express, { ErrorRequestHandler } from 'express'
// カスタムエラーハンドリング
const errorHandler: ErrorRequestHandler = (err, req, res, next) => {
if (err instanceof SyntaxError) {
res.status(400).send({ error: 'Invalid JSON format' });
return
}
next(err);
};
app.use(errorHandler);
SyntaxError
が発生した場合のエラーメッセージを定義しました。
curl -X POST http://localhost:3000/api/data -H "Content-Type: application/json" -d '{"name": "Alice", "age": 25,'
{"error":"Invalid JSON format"}
構文エラーに関してはエラーハンドリングできましたが実際のアプリケーションでは様々な理由でエラーが発生しレスポンスを正しく返せないケースがあります。
わかりやすい例
app.get('/error', (req, res) => {
throw new Error('Something went wrong!');
});
curl http://localhost:3000/error
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>Error: Something went wrong!<br> at /Users/test/Documents/workspace/express-basics-guide/server.ts:74:9<br> at Layer.handle [as handle_request] (/Users/test/Documents/workspace/express-basics-guide/node_modules/express/lib/router/layer.js:95:5)<br> at next (/Users/test/Documents/workspace/express-basics-guide/node_modules/express/lib/router/route.js:149:13)<br> at Route.dispatch (/Users/test/Documents/workspace/express-basics-guide/node_modules/express/lib/router/route.js:119:3)<br> at Layer.handle [as handle_request] (/Users/test/Documents/workspace/express-basics-guide/node_modules/express/lib/router/layer.js:95:5)<br> at /Users/test/Documents/workspace/express-basics-guide/node_modules/express/lib/router/index.js:284:15<br> at Function.process_params (/Users/test/Documents/workspace/express-basics-guide/node_modules/express/lib/router/index.js:346:12)<br> at next (/Users/test/Documents/workspace/express-basics-guide/node_modules/express/lib/router/index.js:280:10)<br> at Layer.handle [as handle_request] (/Users/test/Documents/workspace/express-basics-guide/node_modules/express/lib/router/layer.js:91:12)<br> at trim_prefix (/Users/test/Documents/workspace/express-basics-guide/node_modules/express/lib/router/index.js:328:13)</pre>
</body>
</html>
このようなケースのために汎用的なエラーハンドリングを用意しておきます。
// カスタムエラーハンドリング
const errorHandler: ErrorRequestHandler = (err, req, res, next) => {
if (err instanceof SyntaxError) {
res.status(400).send({ error: 'Invalid JSON format' });
return
}
- next(err);
+ console.error(err); // エラーログを記録(開発中に役立つ)
+ res.status(500).send('Internal Server Error')
+ return
};
app.use(errorHandler);
このエラーハンドリングミドルウェアはapp.get
などのルーティングの後に記述します。また、app.use
の中でも最後によばれるよう定義する必要があります。
curl http://localhost:3000/error
{"error":"Internal Server Error"}
ここまで書いてきたserver.ts
です。
server.ts
import express, { ErrorRequestHandler } from 'express'
const app = express();
const router = express.Router();
// ログ記録用ミドルウェア
app.use((req, res, next) => {
console.log(`${req.method} ${req.url}`);
next();
});
// JSON解析ミドルウェアを設定
app.use(express.json());
app.get('/user/:id', (req, res, next) => {
console.log(`Request for user ID: ${req.params.id}`);
next();
}, (req, res) => {
res.send(`User ID: ${req.params.id}`);
});
app.get('/', (req, res) => {
res.send('Hello, Express');
});
app.get('/users/:id', (req, res) => {
const userId = req.params.id;
res.send(`User ID: ${userId}`);
});
app.get('/search', (req, res) => {
const query = req.query.q;
res.send(`Search query: ${query}`);
});
app.post('/submit', (req, res) => {
res.send('Form submitted!');
});
app.post('/api/data', (req, res) => {
console.log(req.body);
res.send(`Received: ${JSON.stringify(req.body)}`);
});
router.get('/profile', (req, res) => {
res.send('User profile');
});
router.get('/settings', (req, res) => {
res.send('User settings');
});
app.use('/user', router);
app.get('/test', (req, res) => {
res.send('Hello from Handler 1');
});
app.get('/test', (req, res) => {
res.send('Hello from Handler 2');
});
app.get('/error', (req, res) => {
throw new Error('Something went wrong!');
});
// カスタムエラーハンドリング
const errorHandler: ErrorRequestHandler = (err, req, res, next) => {
if (err instanceof SyntaxError) {
res.status(400).send({ error: 'Invalid JSON format' });
return
}
console.error(err); // エラーログを記録(開発中に役立つ)
res.status(500).send({ error: 'Internal Server Error' })
return
};
app.use(errorHandler);
app.listen(3000, () => {
console.log('Server running on http://localhost:3000');
});
おすすめ記事
さいごに
この記事では、Node.jsのExpressフレームワークを使ったWebアプリケーション開発の基本的な部分について学んできました。Expressの魅力は、シンプルでありながら非常に強力な機能を持っているところです。この記事を参考に、実際に自分のプロジェクトにExpressを導入してみてください。実際の開発で使いこなすためには、いくつかの実践的なプロジェクトに取り組むことが重要です。これからもNode.jsとExpressの学習を続けて、効率的なWeb開発スキルを磨いていきましょう。
関連記事
- ExpressとMongoDBで簡単にWeb APIを構築する方法【TypeScript対応】
2024/12/09 - フロントエンド開発に役立つモックサーバー構築:@graphql-tools/mock と Faker を使った実践ガイド
2024/12/25 - Supertest と Jest を活用した Express + MongoDB アプリのエンドツーエンドテスト解説
2024/12/20 - TypeScriptで始めるApollo Server入門:Express & MongoDB連携ガイド
2024/12/25 - JestとTypeScriptで始めるテスト自動化:基本設定から型安全なテストの書き方まで徹底解説
2023/09/13 - Next.jsとAuth.jsで認証機能を実装するチュートリアル
2024/09/13 - 【Next.js】フロントエンド開発で欠かせないReactのUIコンポーネントのテストをReact Testing Libraryで実装
2023/09/20 - React + TypeScript + Webpackでバンドル環境を作るステップバイステップガイド
2025/01/05